The final thing you may ask is why you should go through all the trouble with JDBC code when multiple Object-Relational Mapping (ORM) tools greatly simplify the job. The answer is that JDBC allows you to fully harvest the power of the database you are using. Several excellent ORM tools (iBATIS and Hibernate) are covered in this book, but none give you the power to fully control the data selection and object creation. This may not be a major issue for simple applications, but if you're implementing a more complex application, you may want to implement various caching models. True, both iBATIS and Hibernate support caching, but the caching engine in your application may be far too complex for ORM tools.
To be more specific, iBATIS allows you to forget all about JDBC inner classes and manually code the SQL statements, which makes all nonstandard features of your database easily accessible. However, there are several drawbacks, such as your inability to implement custom enumerations.
Consider a situation where you have a relatively small and fixed number of user roles in your system. Let's consider just three roles: administrator, user, and guest. You want to represent these roles as UserRole domain objects. However, you do not want to create an instance of UserRole for each user because each individual UserRole should only exist once on the heap. There is no reason to be creating 1,000 identical UserRole objects for 1,000 users! In JDBC, you can easily use canonicalization to do this, assigning an appropriate single instance of UserRole in the mapRow() method.
Hibernate has support for custom enumerations, which can be used to model this scenario, but it has limited support for custom SQL queries. Furthermore, it may create very complex object graphs, especially when dealing with tree structures. If you need to model something similar to a directory structure, you would probably define the table to store the directory name and parent ID as a foreign key, and the domain object to store the name and another directory domain object as a parent. To make sure all references are solved, Hibernate needs to build the entire tree, which results in a lot of heap space being used and a lot of unnecessary database work.