Spring's support for specific O/R mapping strategies covers two main areas: design and implementation of data access objects, and convenient transaction management. The basic patterns apply to all of theO/R mapping tools mentioned.
Important | In general, Spring provides a consistent usage model for persistence: common DAO design and implementation, common setup style in a Spring context, and common transaction management. As you will see, Spring's O/R mapping support basically follows the design of the JDBC support. It provides as much conceptual commonality as possible, while still retaining the full power of the concrete underlying tool. |
You have already learned about Spring's generic DataAccessException hierarchy, which facilitates the design of implementation-agnostic DAO interfaces, in Chapter 5. All of the O/R mapping integration classes that come with the Spring distribution follow the same pattern: That is, they allow for being used behind application-specific DAO interfaces that consistently throw DataAccessExceptions. Thus, callers do not usually need to be aware of the implementation strategy used by a DAO. As you willsee, this is important if there is a possibility that you might ever want to switch between O/R mapping technologies.
All of Spring's O/R mapping integration classes aim to provide sophisticated exception translation as far as possible. They will convert O/R mapper exceptions to proper DataAccessException subclasses such as DataIntegrityViolationException or OptimisticLockingFailureException, and translate underlying SQLExceptions via a Spring SQLExceptionTranslator. This allows callers to catch specific subclasses of DataAccessException, without special regard to the chosen persistence tool.
As discussed in detail in Chapter 10 of J2EE without EJB, it is usually not feasible to design DAO interfaces that are supposed to be used with either plain JDBC or a full-blown O/R mapping tool. DAO interfaces for Hibernate and JDO usually just operate on first-class objects, relying on cascading to dependent objects, and just require store calls for reattaching persistent objects to new transactions — in contrast to JDBC-oriented DAOs, which tend to offer fine-grained operations without implicit cascading.
DAO interfaces designed for a transparent persistence backend have the choice to offer loosely typed operations (such as store and delete operations that take java.lang.Object) or strongly typed operations(such as store and delete operations on application-specific, first-class domain objects). While the former reduce the number of exposed operations, they do not clearly indicate which persistent objects are supposed to be created, updated, or deleted. Therefore, we recommend designing such DAO interfaces as strongly typed, even if the implementation delegates to loosely typed Hibernate or JDO operations.
Important | The DAO pattern usually offers clear benefits even with O/R mapping tools underneath. It conceals the use of database-specific query optimizations or vendor-specific JDO extensions, and allows for easy mock implementations of the application-specific DAO interfaces. We recommend designing such DAO interfaces as strongly typed, to make the available operations and their target classes immediately obvious, and to ease mocking of specific operations. |
For all supported O/R mapping strategies, Spring provides pre-built template classes to ease DAO implementations, analogous to the JdbcTemplate class for JDBC. Any kind of work can be performed via the native API of the underlying tool through custom callback implementations, without having to care about resource opening/closing, transaction participation, and exception conversion. Furthermore, these template classes offer a wide variety of convenience operations, which reduce typical DAO method implementations to one-liners, as you will see below.
Spring provides two kinds of transaction support for O/R mapping tools:
Automatic synchronization of local O/R mapper contexts such as a Hibernate Session or a JDO PersistenceManager with global JTA transactions, usually demarcated via Spring's JtaTransactionManager. This is an alternative to deploying special JCA connectors for your O/R mapping tool, which achieve a similar goal.
Local transaction strategies for single databases, in the form of one implementation of Spring's PlatformTransactionManager interface per particular O/R mapping tool (for example, HibernateTransactionManager or JdoTransactionManager). Such a strategy will work completely without JTA, for example in Tomcat or a standalone application.
With proper setup, neither the DAO implementations nor the transaction demarcation code has to change when switching between global and local transactions — a simple configuration change is enough. Usually, this has to coincide with the backend JDBC DataSource choice: In the global transaction case, these will be J2EE container DataSources; in the local transaction case, these will often be local DataSources such as a Commons DBCP BasicDataSource or a C3P0 ComboPooledDataSource.
For general details on Spring's transaction infrastructure and setup of global transactions or local transactions, including appropriate choices for JDBC DataSources, see Chapter 6.