Hibernate


Hibernate is probably the most popular O/R mapping tool in early 2005. Its two key features are the query language HQL and automatic change detection through snapshot comparisons. In contrast to iBATIS SQL Maps, Hibernate does abstract the underlying database and its data model, generating SQL under the hood rather than letting the user work at the SQL level. In combination with Hibernate's very lenient lifecycle for persistent objects, the resulting programming model is very convenient, while still being able to adapt to existing data models as far as possible.

Hibernate's query language, HQL, offers important relational concepts such as joins and aggregate functions. In general, HQL is closer to SQL than to other object query languages, with the important difference from SQL that queries are expressed in terms of domain object properties rather than database column values, thus decoupling from the database schema. In many respects, HQL allows developers to leverage SQL's power at the domain object level. It is also relatively easy to learn for developers already familiar with SQL.

Hibernate performs change detection via snapshot comparisons. With its optimized reflection usage via CGLIB — creating runtime proxies for persistent objects through dynamic byte code generation — the performance overhead of such comparisons is not as high as might be assumed. The advantage of Hibernate's model over JDO-style change notifications is that Hibernate does not need to modify persistent objects to observe their state.

Working with detached objects is easy because of Hibernate's lenient lifecycle for persistent objects. A persistent object will seamlessly switch from managed to non-managed when a Hibernate Session gets closed, without an explicit detachment step. An object can then get modified outside of a Hibernate Session, for example in a form workflow in a web application, and subsequently become reattachedto a new Hibernate Session (through a saveOrUpdate operation), which will store the current state of the object.

Hibernate supports pluggable cache strategies, providing out-of-the-box implementations for EHCache, OSCache, SwarmCache, and JBoss TreeCache. Hibernate applies fine-granular caching of individual objects per ID and optionally also of query results. Modifications performed via Hibernate will automatically update affected objects and affected query results in the cache; timeouts are necessary only if other processes are also modifying the database.

For detailed information on Hibernate mapping options, query syntax, and so on, please refer to the excellent documentation in the Hibernate distribution and/or to Gavin King and Christian Bauer's Hibernate in Action (Manning, 2004) or another book on Hibernate. Hibernate is a very powerful tool with a wide variety of features; it is beyond the scope of this book to cover it in a comprehensive fashion. Rather, we will concentrate on integration into a Spring environment and on added value provided by Spring.

Hibernate is available from www.hibernate.org, licensed under the LGPL. At the time of this writing, the stable branch was Hibernate 2.1, which all examples refer to. Hibernate 3.0 is scheduled for early 2005; because of a package name change from net.sf.hibernate to org.hibernate, Spring will provide separate support for it in the org.springframework.orm.hibernate3 package. In most respects, Hibernate3 support will be an exact mirror of the Hibernate 2.1 support, but in the orm.hibernate3 package rather than the orm.hibernate package. Thus the following discussion is equally relevant to Hibernate 3 as Hibernate 2 — only the package name changes.

Mapping File

A Hibernate mapping file contains definitions for classes, mapping them to database tables, with each property in the persistent class corresponding to a column in the database table. At runtime, Hibernate will generate SQL statements from those mappings; the same mapping definition is used for creating query, insert, update, and delete statements. All mapped classes are in principle assumed to be read-write; non-mutable objects can be marked as such, but are in many respects treated just like mutable objects.

The following is an excerpt from a simple Hibernate mapping file, taken from the PetClinic sample application:

<class name="org.springframework.samples.petclinic.Vet" table="vets">   <id name="id" column="id" unsaved-value="-1">     <generator />   </id>   <property name="firstName" column="first_name"/>   <property name="lastName" column="last_name"/>   <set name="specialtiesInternal" table="vet_specialties">     <key column="vet_id"/>     <many-to-many column="specialty_id"         />   </set> </class>     <class name="org.springframework.samples.petclinic.Specialty" table="specialties">   <id name="id" column="id" unsaved-value="-1">     <generator />   </id>   <property name="name" column="name"/> </class> 

Essentially, each class mapping contains properties that map to database columns, and association properties that interpret a database column as a foreign key. Such associations will be resolved into objects at load time. The persistent objects do not need to hold foreign key IDs or the like, but can be designed for pure object-to-object associations, no matter whether 1:1, 1:n, or m:n. Properties can either be accessed through JavaBean property setters and getters or through direct access to the instance fields.

ID generation is abstracted by Hibernate. The mapping file defines the generation strategy to use. Among the supported strategies are native database features such as auto-increment columns (for example on MySQL) and sequences (for example on Oracle), but also various flavors of UUID generation (in the JVM). To make database-specific strategies work, a Hibernate "dialect" needs to be configured. This is done at the SessionFactory level, not in the mapping file, as we will discuss later.

Usage of Hibernate in Spring does not impose any special requirements. The mapping files are usually completely independent from the actual usage style; they can be seamlessly used with a plain Hibernate API or with Spring's Hibernate support.

Important 

As a full-blown O/R mapping tool, Hibernate works at the persistent class level, rather than at the statement level. The corresponding SQL statements are generated under the hood. Database specifics such as identity generation are abstracted through a configurable database "dialect."

DAO Implementation

Direct Hibernate usage involves a resource factory, net.sf.hibernate.SessionFactory, which is used to open a single-threaded resource, net.sf.hibernate.Session, for each transaction or sequence of operations. This is roughly analogous to a javax.sql.DataSource and a java.sql.Connection, respectively, in the case of JDBC. Non-Spring Hibernate code usually manages such Sessions in a manual fashion, associating them with the scope of a transaction or the scope of a web request.

In a Spring environment, application code does not usually manage Hibernate Sessions manually — a major boon for developers. Spring provides convenience classes that remove the burden of resource management from the application developer. Most important, Hibernate Sessions will automatically be synchronized with Spring transactions. In other respects, the usual conventions apply: DAO operations will throw Spring's generic DataAccessException, and DAOs are usually set up as beans in a Spring context.

The template class for data access operations is org.springframework.orm.hibernate.HibernateTemplate, working with Hibernate's Session API underneath. It is typically used through the base class org.springframework.orm.hibernate.support.HibernateDaoSupport, which takes a Hibernate SessionFactory instance as a bean property and provides a HibernateTemplate instance for it. (This is not a requirement, though: HibernateTemplate can also be instantiated directly, just like JdbcTemplate.)

For example, the following is a Hibernate-based DAO from the PetClinic sample application:

 public class HibernateClinic extends HibernateDaoSupport  implements Clinic {       public Collection getVets() throws DataAccessException {     return getHibernateTemplate().find(         "from Vet vet order by vet.lastName, vet.firstName");   }       public Collection getPetTypes() throws DataAccessException {     return getHibernateTemplate().find("from PetType type order by type.name");   }       public Collection findOwners(String lastName) throws DataAccessException {     return getHibernateTemplate().find(         "from Owner owner where owner.lastName like ?", lastName + "%");   }       public Owner loadOwner(int id) throws DataAccessException {     return (Owner) getHibernateTemplate().load(Owner.class, new Integer(id));   }       public Pet loadPet(int id) throws DataAccessException {     return (Pet) getHibernateTemplate().load(Pet.class, new Integer(id));   }       public void storeOwner(Owner owner) throws DataAccessException {     getHibernateTemplate().saveOrUpdate(owner);   }       public void storePet(Pet pet) throws DataAccessException {     getHibernateTemplate().saveOrUpdate(pet);   }       public void storeVisit(Visit visit) throws DataAccessException {     getHibernateTemplate().saveOrUpdate(visit);   } } 

Without the provided convenience base class, the implementation could implement its own setSessionFactory method, preferably creating a shared HibernateTemplate instance for the DAO:

 public class HibernateClinic implements Clinic {       private HibernateTemplate hibernateTemplate;       public void setSessionFactory(SessionFactory sessionFactory) {     this.hibernateTemplate  = new HibernateTemplate(sessionFactory);   }       public Collection getVets() throws DataAccessException {     return this.hibernateTemplate.find(     "from Vet vet order by vet.lastName, vet.firstName");   }       public Collection getPetTypes() throws DataAccessException {     return this.hibernateTemplate.find("from PetType type order by type.name");   }   public Collection findOwners(String lastName) throws DataAccessException {     return this.hibernateTemplate.find(         "from Owner owner where owner.lastName like ?", lastName + "%");   }       ... }

Alternatively, a DAO implementation could also use the provided SessionFactory instance directly, without Spring's HibernateTemplate. However, this would involve manual handling of Hibernate Sessions, manual exception translation, and manual synchronization with Spring transactions; therefore, it is usually advisable to go through HibernateTemplate. If necessary, helpers for manual DAO implementations can be found in the org.springframework.orm.hibernate.SessionFactoryUtils class:

public class HibernateClinic implements Clinic {       private SessionFactory sessionFactory;       public void setSessionFactory(SessionFactory sessionFactory) {     this.sessionFactory  = sessionFactory;   }       public Collection getVets() throws DataAccessException {     Session session = SessionFactoryUtils.getSession(this.sessionFactory    , true);     try {       return session.find("from Vet vet order by vet.lastName, vet.firstName");     }     catch (HibernateException ex) {       throw SessionFactoryUtils.convertHibernateAccessException(ex);     }     finally {       SessionFactoryUtils.closeSessionIfNecessary(session, this.sessionFactory);     }   }       ... }

The operations defined on HibernateTemplate basically correspond to operations on net.sf.hibernate.Session, but throw Spring's DataAccessException rather than Hibernate's checked HibernateException and participate in Spring transactions (if any). Please refer to the Hibernate documentation for details on the semantics of those operations. HibernateTemplate defines a wide variety of convenience versions of those methods. Many of them are not available on a plain Hibernate Session, so HibernateTemplate has a significant convenience value, besides its other benefits.

As an alternative to invoking such operations on HibernateTemplate itself, there is the option to implement an org.springframework.orm.hibernate.HibernateCallback that works directly on a given Hibernate Session resource, via HibernateTemplate's generic execute method. This is rarely necessary for typical one-line operations; however, complex Hibernate queries or processing operations should be implemented in a HibernateCallback, in particular if they rely on getting executed on the same Hibernate Session. The callback approach should be familiar from Spring's approach to JDBC and other APIs:

public class HibernateClinic extends HibernateDaoSupport implements Clinic {       public Collection getVets() throws DataAccessException {     return getHibernateTemplate().executeFind(       new HibernateCallback() {         public void doInHibernate(Session session) throws HibernateException {           Query query = session.createQuery               "from Vet vet order by vet.lastName, vet.firstName");           // further customization of the query object           return query.list();         }       });     }   }       ... }

Note that Hibernate has a special requirement for lazy loading. The Hibernate Session that originally loaded the affected persistent objects needs to still be open to make lazy loading work. As HibernateTemplate by default closes the Hibernate Session at the end of each operation, returned objects are not capable of lazy loading. It is recommended to execute such operations within transactions, which will keep the same Hibernate Session open for the lifetime of the entire transaction. If you need lazy loading beyond the scope of your transactions — for example in web views — you need to resort to the Open Session in View pattern, discussed later in this chapter.

All such a DAO needs to be able to work is a Hibernate SessionFactory instance via the setSessionFactory method. In the next section, we will see how to set up such an instance in a Spring context.

Setup in a Spring Context

In a Spring application context, Hibernate is usually set up via Spring's LocalSessionFactoryBean, referring to the JDBC DataSource to use and the Hibernate mapping files to load. Typically, there are also a couple of Hibernate properties defined: Most important, the database "dialect" to use:

<bean      >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="mappingResources">     <value>petclinic.hbm.xml</value>   </property>   <property name="hibernateProperties">     <props>       <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>     </props>   </property> </bean> 

The "mappingResources" entry in the preceding code refers to a classpath resource. Other ways of loading mapping files are "mappingLocations", accepting any Spring location string, for example to load from the WEB-INF directory of a web application:

<bean      >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="mappingLocations">     <value>/WEB-INF/petclinic.hbm.xml</value>   </property>   <property name="hibernateProperties">     <props>       <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>     </props>   </property> </bean>

Further options to load mapping files from include "mappingJarLocations" and "mappingDirectoryLocations", loading all mapping files contained in given JAR files or in given directories. These correspond to operations on net.sf.hibernate.Configuration, as documented in the Hibernate reference manual.

Alternatively, you can also use a standard hibernate.cfg.xml file to configure your SessionFactory, specifying the location as "configLocation". Mapping files and Hibernate properties are usually defined in that separate file in such a scenario, although those could also be mixed with LocalSessionFactoryBean properties.

<bean      >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="configLocation">     <value>/WEB-INF/hibernate.cfg.xml</value>   </property> </bean>

An advantage of defining properties in the Spring context is that you can use placeholders for certain properties, linking in values from configuration properties files (via Spring's PropertyPlaceholderConfigurer mechanism, see Chapter 2). So even when loading configuration from a hibernate.cfg.xml file, it is advisable to define such administrative properties on LocalSessionFactoryBean in the Spring context.

When using Hibernate in a Spring environment, resource and transaction management is usually the responsibility of Spring. Hibernate configuration should therefore not contain any transaction-related properties. In particular, the choice between local transactions and JTA transactions should be deferred to the Spring transaction manager. We will discuss transactions further in the next section.

The DAO can now receive a reference to the Hibernate SessionFactory instance via a Spring bean reference, just like a JDBC-based DAO receives a reference to a JDBC DataSource.

<bean      >   <property name="sessionFactory">     <ref bean="sessionFactory"/>   </property> </bean>

Whether the sessionFactory property, corresponding to the setSessionFactory method, is defined by the provided HibernateDaoSupport base class or implemented by the DAO class itself is not relevant here. The same configuration would work in both scenarios.

Setup as a JCA Connector

As an alternative to setting up a LocalSessionFactoryBean in a Spring context, a SessionFactory can also be set up as a server-wide resource in a J2EE environment, shared by multiple applications. Such a global SessionFactory can be linked in as a JNDI resource, replacing a LocalSessionFactoryBean definition, using Spring's standard JNDI lookup Factory Bean, as follows:

<bean      >   <property name="jndiName">     <value>java:/mySessionFactory</value>   </property> </bean>     <bean      >   <property name="sessionFactory">     <ref bean="sessionFactory"/>   </property> </bean>

While it is possible to use a server-specific startup class to register such JNDI resources, this is not portable. The only standard J2EE way to set up such a shared resource is through a JCA connector: that is, by deploying a connector that follows the J2EE Connector Architecture. Such resources can then usually be managed centrally in the J2EE server's management console, including fine-grained settings if they expose JMX properties.

JCA connectors are an appropriate way to plug drivers for non-relational backend resources into a J2EE server, especially if they require connection pooling and are supposed to participate in global transactions. However, we do not feel that JCA is an appropriate way to deploy an O/R mapping resource such as a Hibernate SessionFactory, for the following reasons:

  • Mappings between database tables and domain objects of a particular application should be considered as part of the application, not as a shared resource in the server.

  • Hibernate Sessions are not pooled separately; pooling of the underlying JDBC Connections is perfectly sufficient.

  • Hibernate Sessions do not represent physical resources that need to participate in the XA protocol for global transactions; this just applies to the underlying JDBC Connections.

The Hibernate JCA connector does not leverage specific features of the JCA container, and inappropriately moves domain-specific mapping logic out of the application. Furthermore, deployment of a JCA connector involves server-specific parameterization and extra deployment steps — for little benefit.

Important 

We recommend defining a Hibernate SessionFactory as a local resource within the application — where it logically belongs — using Spring's LocalSessionFactoryBean. Note that the choice between a local JDBC DataSource and a shared DataSource from JNDI is a separate decision. A local Hibernate SessionFactory can work with a shared J2EE server DataSource, using either JTA or local transactions.

Transaction Management

While the Hibernate Session API provides methods for transaction management, they are not normally used in a Spring environment (and are not intended for use in a J2EE environment). Instead, transaction management is delegated to Spring's generic transaction facilities, with the generic TransactionTemplate or TransactionProxyFactoryBean used for demarcation.

The following choices are available as backend transaction strategies for Hibernate. As with iBATIS and other data access APIs, we essentially have the choice between Spring's dedicated transaction manager for that API and the global JTA transaction manager:

  • org.springframework.orm.hibernate.HibernateTransactionManager: Allows transaction execution on a single Hibernate SessionFactory that is associated with a single target database, using native Hibernate facilities. Hibernate-based DAOs can seamlessly participate in such transactions. JDBC-based DAOs can join in, provided that the JDBC DataSource is available to the transaction manager (it is auto-detected when specified on LocalSessionFactoryBean). This strategy is usually sufficient as long as there is no need for transactions that span multiple transactional resources.

  • org.springframework.transaction.jta.JtaTransactionManager: Delegates transaction execution to a JTA implementation: that is, to a J2EE server's JTA subsystem or a JTA implementation such as ObjectWeb's JOTM. HibernateTemplate-based DAOs will automatically participate in such transactions, as long as the JDBC DataSource is JTA-aware (usually defined as XA DataSource in the J2EE server). This strategy allows the execution of transactions across multiple transactional resources, for example across multiple database systems. See Chapter 6 for details on when to choose JTA.

In both cases, Hibernate Sessions will automatically be associated with transactions: one Hibernate Session for the entire scope of a transaction. Data access code will automatically receive the transactional Session if executing within a transaction; otherwise, a short-lived Session will be opened for each operation. The only exception to this rule is when applying the Open Session in View pattern in single session mode (see the upcoming "Session Lifecycle" section for a further discussion of the Session lifecycle).

In the case of suspended transactions, each transaction will be associated with its own Hibernate Session. So when a new inner transaction starts, the Hibernate Session of the outer transaction will be suspended and a new one will be created. On completion of the inner transaction, the inner Hibernate Session will be closed and the outer one will be resumed. This will work with both HibernateTransactionManager and JtaTransactionManager, leveraging Spring's transaction synchronization mechanism.

Important 

If there is a need to perform transactions across multiple resources, choose JtaTransactionManager, which delegates to the J2EE server's JTA subsystem (or a locally defined transaction coordinator such as JOTM). However, for transactions on a single target database, HibernateTransactionManager is perfectly sufficient — and will work in any environment, whether a J2EE web application, a standalone application, or a test suite.

For example, a transactional proxy for the DAO as previously defined could look as follows:

<bean      >   <property name="sessionFactory">     <ref bean="sessionFactory"/>   </property> </bean>     <bean      >   <property name="sessionFactory">     <ref bean="sessionFactory"/>   </property> </bean>     <bean    >   <property name="transactionManager">     <ref bean="transactionManager"/>   </property>   <property name="target">     <ref bean="clinicDaoTarget"/>   </property>   <property name="transactionAttributes">     <props>       <prop key="store*">PROPAGATION_REQUIRED</prop>       <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>     </props>   </property> </bean>

Please refer to Chapter 6 for details on transaction proxy definitions. This configuration example uses Spring's HibernateTransactionManager, which is sufficient for accessing a single database via Hibernate.

JTA Synchronization

As outlined previously, Spring's JtaTransactionManager — Spring's PlatformTransactionManager implementation for JTA — works well with Hibernate, using Spring's own transaction synchronization facility to associate a Hibernate Session with each transaction. This is the recommended usage stylefor Spring on JTA: Spring-driven transactions, demarcated via the generic TransactionTemplate or TransactionProxyFactoryBean, with JtaTransactionManager as the backend strategy. No further configuration is necessary here; in particular, there is usually no need for JTA-related properties in Hibernate configuration.

As a special feature of Spring's Hibernate support, Hibernate Sessions can also be associated with plain JTA transactions or EJB-driven transactions, without Spring transaction demarcation being involved. (Essentially, such transactions are always managed by JTA underneath, even when demarcated via EJB CMT.) This allows the use of HibernateTemplate-based DAOs behind EJB facades (with EJB-demarcated transactions) or with plain JTA, still benefiting from transaction-scoped Sessions managed in the background by Spring's Hibernate integration.

Important 

Spring's HibernateTemplate is able to synchronize with either Spring-driven transactions or JTA transactions, which includes EJB-driven transactions. A DAO that uses HibernateTemplate does not have to be modified or reconfigured for the actual strategy: It will automatically participate in whatever transaction it encounters.

Direct JTA synchronization will work only if the Hibernate SessionFactory is associated witha JTA TransactionManager (javax.transaction.TransactionManager). This requires a TransactionManagerLookup class specified in Hibernate configuration, which is specific to the application server (or the standalone JTA implementation). In contrast to the JTA UserTransaction, there is unfortunately no standard JNDI location for the JTA TransactionManager.

<bean      >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="mappingLocations">     <value>/WEB-INF/petclinic.hbm.xml</value>   </property>   <property name="hibernateProperties">     <props>       <prop key="hibernate.dialect">         net.sf.hibernate.dialect.OracleDialect       </prop>       <prop key="hibernate.transaction.manager_lookup_class">         net.sf.hibernate.transaction.WeblogicTransactionManagerLookup       </prop>     </props>   </property> </bean> 

Alternatively, you can also specify the javax.transaction.TransactionManager as a bean reference, passing it into LocalSessionFactoryBean's jtaTransactionManager property. The TransactionManager reference can be obtained from a server-specific JNDI location (for example, "javax.transaction.TransactionManager" in the case of WebLogic) or defined as a special Factory Bean (for example, org.springframework.transaction.jta.WebSphereTransactionManagerFactoryBean).

<bean      >   <property name="jndiName">     <value>javax.transaction.TransactionManager</value>   </property> </bean>     <bean      >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="jtaTransactionManager">     <ref bean="jtaTransactionManager"/>   </property>   <property name="mappingLocations">     <value>/WEB-INF/petclinic.hbm.xml</value>   </property>   <property name="hibernateProperties">     <props>       <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>     </props>   </property> </bean>

Such a jtaTransactionManager bean can, for example, be shared with Spring's JtaTransactionManager (the Spring's PlatformTransactionManager implementation for JTA), which needs the javax.transaction.TransactionManager reference to perform transaction suspension. This allows for setting up the special location of the JTA TransactionManager once and sharing it, rather than specifying it multiple times (once in Hibernate configuration, once as a Spring bean for other purposes).

Spring's HibernateTemplate will then automatically detect the JTA TransactionManager and register appropriate synchronizations for flushing and closing transaction-scoped Hibernate Sessions on transaction completion. Association of an individual Hibernate Session per transaction in case of suspended transactions will work too; usually, such suspension happens through EJB CMT (for example, with propagation code REQUIRES_NEW), as the JTA UserTransaction does not support transaction suspension.

Specifying the JTA TransactionManager for Hibernate is not necessary when solely using Spring- driven transactions; Spring's own transaction synchronization mechanism will then be used to properly close the Hibernate Session after completion. However, some J2EE servers are very strict in what kind of JDBC operations they allow after transaction completion. If encountering warnings in your log, specifying a TransactionManagerLookup will help, as it leads to internal JTA synchronization of the Hibernate Session itself (which should never interfere with the J2EE server's resource handling).

Important 

With Spring-driven transactions, it is usually not necessary to specify JTA-related properties in Hibernate configuration, as Spring will automatically care for proper transaction completion (including correct handling of a transactional second-level cache). However, it might still add value to make Hibernate aware of the JTA TransactionManager, to avoid warnings on J2EE servers with strict transactional connection pools.

Session Lifecycle

Understanding the lifecycle of a Hibernate Session and the side effects of its behavior can be very important for tracking down problems in data access operations. While Spring does manage Sessions implicitly, with a variety of options, application code still needs to respect the side effects.

As discussed, Spring usually associates Sessions with transactions. Each transaction will use its own Hibernate Session. More specifically, each transaction will use one transactional Hibernate Session per SessionFactory, that is, one Session per target database. So in the case of a JTA transaction, multiple Sessions might be involved in a single transaction — one per SessionFactory.

A Hibernate Session effectively serves as a transactional cache for persistent objects. It holds references to all loaded objects in order to wire them correctly in case of bidirectional associations, and to check them for changes on transaction commit when flushing the Session. On flush, SQL statements will be issued to synchronize the in-memory state with the database state. A flush can also be triggered explicitly, to make other data access code see the changes within the same transaction. (This is particularly useful if it is necessary to mix Hibernate usage with JDBC queries.)

One Hibernate Session per transaction is usually a good fit. The Session will represent all objects loaded within the transaction, in a consistent fashion. At transaction completion, all changes will be flushed and committed in a single database transaction. In the case of a transaction rollback, the Hibernate Session will carry modifications that have been rolled back at the database level; this does not matter in case of a transaction-scoped Session, which will be closed at transaction completion.

The only common case to extend the Session lifecycle beyond the scope of a transaction is the Open Session in View pattern, discussed in its own section later in this chapter. In such a scenario, the Hibernate Session will not be closed at transaction completion; therefore, it needs to be cleared in case of a rollback, throwing away all modifications carried by the Session. Spring's transaction management will automatically perform such clearing when appropriate.

Reattachment and the Duplicate Object Problem

A common problem when using transaction-scoped Sessions is reattaching a persistent object to a new transaction via a saveOrUpdate operation, for example coming in from a web session where the object has been edited in a form. This needs to happen as early as possible in the transaction. Otherwise, Hibernate might complain that an object with the given ID has already been loaded in the current Session — the duplicate object problem. A Hibernate Session is a strict cache for persistent objects. It does not allow for replacing an existing persistent object with a different instance that represents the same persistent entity.

Note that saveOrUpdate actually serves two purposes. It can be used to reattach a persistent object to a new transaction, but also to persist a new instance for the first time. This is very convenient for typical use cases in a web application, where a form controller can often edit both existing and new instances, without special discrimination. A DAO operation can then simply call saveOrUpdate for a given instance, without explicit case handling for existing and new instances. Furthermore, saveOrUpdate will simply be ignored if the given instance is already attached to the current Hibernate Session; this allows for writing DAO store operations that will work both within and outside a transaction.

Important 

When reattaching an object to a new transaction via saveOrUpdate, make sure that the operation happens as early as possible. In particular, don't perform other operations that might load a representation of the given persistent entity (for the given id) before — explicitly or implicitly.

Hibernate's saveOrUpdateCopy operation addresses the same problem through copying the state over if an object with the given ID already exists, instead of always reattaching the given instance itself. Unfortunately, in general it is not recommended to rely on saveOrUpdateCopy: Associated objects reached through cascading can still cause duplicate object exceptions.

HibernateInterceptor

A special feature for controlling the lifecycle of a Hibernate Session is org.springframework.orm.hibernate.HibernateInterceptor, which can be registered with an AOP proxy to open a Hibernate Session for the scope of a given method invocation. This is just necessary for special scenarios, though; in general, relying on transaction-scoped Sessions will be sufficient.

One possible use case for HibernateInterceptor is to enforce opening a Hibernate Session at the beginning of the transaction. The default behavior in a JTA environment is to register transactional resources on first access, for example in the case of JDBC Connections. It might sometimes be desirable to strictly enforce opening of a Hibernate Session at the beginning of a transaction, which can be configured as a postInterceptor on TransactionProxyFactoryBean:

<bean     >   <property name="transactionManager">     <ref bean="transactionManager"/>   </property>   <property name="target">     <ref bean="clinicDao"/>   </property>   <property name="transactionAttributes">     <props>     <prop key="store*">PROPAGATION_REQUIRED</prop>     <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>     </props>   </property>   <property name="postInterceptors">     <list>       <bean >         <property name="sessionFactory">           <ref bean="sessionFactory"/>         </property>       </bean>     </list>   </property> </bean> 

In the preceding code, HibernateInterceptor is registered as an unnamed inner bean. Of course, it could also be defined as a separate bean, with a bean reference pointing to it.

If a HibernateInterceptor is used to enforce early creation of a Hibernate Session, DAOs can be configured to rely on an existing Session rather than implicitly creating a new one if none is found. This also allows for direct Session access via Spring's org.springframework.orm.hibernate.SessionFactoryUtils class instead of HibernateTemplate. It is not necessary to worry about closing the Session in the DAO implementation in such a scenario, even outside a transaction. However, this is not recommended for typical scenarios, as it ties the DAO to HibernateInterceptor usage and still requires manual exception conversion.

See the section on DAO implementation earlier in the chapter; it gives an example for manual DAO implementations based on SessionFactoryUtils. When applying HibernateInterceptor, the finally block with the closeSessionIfNecessary call can be omitted; the rest of the DAO method implementation would remain. It is recommended that you specify false for getSession's allowCreate flag, though, as the code relies on an existing thread-bound Session now.

public class HibernateClinic implements Clinic {       private SessionFactory sessionFactory;       public void setSessionFactory(SessionFactory sessionFactory) {     this.sessionFactory = sessionFactory;   }       public Collection getVets() throws DataAccessException {     Session session = SessionFactoryUtils.getSession(this.sessionFactory, false);     try {       return session.find("from Vet vet order by vet.lastName, vet.firstName");     }     catch (HibernateException ex) {       throw SessionFactoryUtils.convertHibernateAccessException(ex);     }   }       ...   }

Open Session in View

As outlined earlier, lazy loading will just work as long as the Hibernate Session that originally loaded the persistent object is still open. In the case of a transaction-scoped Session, this means that lazy loading will work only until transaction completion. While it is generally advisable to perform data access within transactions, one major use case requires lazy loading outside transactions: model access inweb views.

In classic web MVC, a controller prepares model attributes, for example fetching them from the DAO layer, and exposes them to a view. The view in turn renders the given model attributes, displaying parts of their state. In the case of persistent domain objects with lazy loading, certain associations might not have been loaded until rendering happens. Unfortunately, lazy loading won't work in such a scenario if the original transaction has already ended and thus closed the Hibernate Session that loaded the given persistent object.

One solution for the problem would be to process the entire HTTP request, including view rendering, within a single transaction. However, such a solution is not ideal: It would hold database locks until view rendering has been completed, which is dependent on the client and network speed. In the case of a large HTML page as result, this can take dozens of seconds. Keeping transactions open for such a long time increases contention and wastes resources, so this is not a recommended strategy.

The common solution is the Open Session in View pattern. Transactions end in the service layer, but the associated Hibernate Session remains open until view rendering has been completed. This releases database locks early but still allows for lazy loading in the view. Spring supports this pattern out of the box, via org.springframework.orm.hibernate.support.OpenSessionInViewFilter (for use with any web tier) or OpenSessionInViewInterceptor (for use with Spring's web MVC framework).

Single Session Versus Deferred Close

OpenSessionInViewFilter and its companion feature two modes of operation: single session mode and deferred close mode. In single session mode, a single Hibernate Session will be used for the entire HTTP request, with transactions operating on the request-scoped Session. In deferred close mode, each transaction will use its own Session as usual, but each of those Sessions will be kept open after transaction completion, to be closed when view rendering has completed.

Single session mode, which is the default, is the most efficient version of the Open Session in View pattern. The request-scoped Hibernate Session serves as first-level cache, loading each persistent object only once within the entire request. The main disadvantage is that all objects managed by that Session are required to be unique. As discussed in the "Session Lifecycle" section, Hibernate insists on all persistent objects being unique in a Session, which can lead to duplicate object exceptions when trying to reattach an object from the HttpSession (for example, when storing the result of a form workflow).

In deferred close mode, the duplicate object problem is avoided by using a fresh Hibernate Session for each transaction. All such Sessions are kept open until view rendering has been completed, to allow for lazy loading on each of them. Unfortunately, this can lead to problems if a single persistent object becomes involved in multiple transactions; Hibernate requires persistent objects (more specifically, their managed collections) to be associated with a specific Hibernate Session, not with two Sessions at the same time. In such a scenario, single session mode is usually preferable.

Important 

In general, try using single session mode first, in particular if the reattaching of persistent objects is not likely to conflict with other data access operations in the same request. If you encounter issues with your use of Open Session in View in single session mode, consider switching to deferred close mode.

It might also be worthwhile to consider avoiding the Open Session in View pattern in the first place. As long as all your lazy associations get initialized within the original transaction, there is no need to actually perform lazy loading in your views. Unfortunately, such pre-loading of all required associations is often fragile; depending on view state, different associations might need to be loaded, imposing knowledge about view state onto the controller. In many applications, it will be more natural and more convenient to allow arbitrary lazy loading through the Open Session in View pattern.

Configuration Examples

The OpenSessionInViewFilter must to be set up in web.xml as a standard Servlet 2.3 filter. The following configuration will apply it to all requests with a URL path that ends with .do:

<filter>   <filter-name>OpenSessionInView</filter-name>   <filter-class>     org.springframework.orm.hibernate.support.OpenSessionInViewFilter   </filter-class> </filter>     <filter-mapping>   <filter-name>OpenSessionInView</filter-name>   <url-pattern>*.do</url-pattern> </filter-mapping>

The filter needs to be able to access the Hibernate SessionFactory, by default, as a bean in the Spring root web application context (where middle tier resources usually reside). The default bean name is sessionFactory; this can be customized through a sessionFactoryBeanName init-param in web.xml.

By default, the filter will operate in single session mode. To configure it for deferred close mode, specify the singleSession init-param as false:

<filter>   <filter-name>OpenSessionInView</filter-name>   <filter-class>     org.springframework.orm.hibernate.support.OpenSessionInViewFilter   </filter-class>   <init-param>     <param-name>singleSession</param-name>     <param-value>false</param-value>   </init-param> </filter>

OpenSessionInViewInterceptor is a HandlerInterceptor for Spring's web MVC, to be registered with a Spring HandlerMapping in a DispatcherServlet context (see Chapter 12 for a discussion of Spring's web MVC):

<bean       >   <property name="sessionFactory">     <ref bean="sessionFactory"/>   </property> </bean>     <bean       >   <property name="interceptors">     <list>       <ref bean="openSessionInView"/>     </list>   </property>   <property name="urlMap">     <map>       <entry key="/myUrlPath"><ref bean="myController"/></entry>     </map>   </property> </bean>

The SessionFactory is passed in as a bean reference here because a HandlerInterceptor can be configured as bean. As the DispatcherServlet context is usually a child of the root web application context, a SessionFactory bean defined there is visible and can be referenced directly.

As with the filter, the interceptor is by default in single session mode. To configure it for deferred close mode, specify the singleSession bean property as false:

<bean       >   <property name="sessionFactory">     <ref bean="sessionFactory"/>   </property>   <property name="singleSession">     <value>false</value>   </property> </bean>

BLOB/CLOB Handling

Spring ships with several Hibernate UserType implementations that map BLOB or CLOB fields in the database to a variety of target property types in persistent classes:

  • org.springframework.orm.hibernate.support.ClobStringType: Mapping a CLOB field in the database to a String property in the persistent class

  • org.springframework.orm.hibernate.support.BlobByteArrayType: Mapping a BLOB field in the database to a byte array property in the persistent class

  • org.springframework.orm.hibernate.support.BlobSerializableType: Mapping a BLOB field in the database to a serializable object property in the persistent class

While Hibernate can, in principle, map BLOBs and CLOBs out of the box, it can do so only with the standard JDBC API. Unfortunately, this has severe limitations on some databases — for example, on Oracle 9i with the Oracle 9i thin driver. For full BLOB and CLOB usage with unlimited content size, native Oracle API has to be used. To allow for handling BLOBs and CLOBs in such database-specific fashions too, the Spring-provided UserTypes delegate to a Spring-managed LobHandler — an abstraction that allows for multiple implementations. Two LobHandlers are provided out of the box:

  • org.springframework.jdbc.support.lob.DefaultLobHandler: Delegating to standard JDBC API. Known to work with, for example, MySQL, DB2, MS SQL Server, and Oracle 10g.

  • org.springframework.jdbc.support.lob.OracleLobHandler: Delegating to Oracle- specific LOB handling API. Necessary for Oracle 9i; with Oracle 10g, DefaultLobHandler will work, too.

See Chapter 5 for a discussion of Spring's LobHandler abstraction in the context of JDBC.

All of the Spring-provided LOB UserTypes depend on a LobHandler being specified on LocalSessionFactoryBean. On initialization, each UserType object will fetch the LobHandler from there:

<bean org.springframework.jdbc.support.lob.OracleLobHandler"/>     <bean       >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="lobHandler">     <ref bean="lobHandler"/>   </property>   <property name="mappingLocations">     <value>/WEB-INF/petclinic.hbm.xml</value>   </property>   <property name="hibernateProperties">     <props>       <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>     </props>   </property> </bean>

Additionally, all LOB UserTypes depend on being executed within transactions when modifying LOBs, to be able to synchronize the closing of temporary LOBs (if any) with transaction completion. This applies to both writing to LOBs and reading from LOBs. It is not strictly necessary to perform a full database transaction ("PROPAGATION_REQUIRED") for this: A Spring-demarcated transaction with"PROPAGATION_SUPPORTS" is sufficient, as it offers transaction synchronization even without an existing full database transaction.

If not used with Spring-driven transactions but rather with plain JTA transactions or EJB-driven transactions, the JTA TransactionManager needs to be specified on LocalSessionFactoryBean. This is the same setting that is necessary for direct JTA synchronization of Hibernate Sessions (see the "JTA Synchronization" section earlier in the chapter).

<bean       >   <property name="dataSource">     <ref bean="dataSource"/>   </property>   <property name="jtaTransactionManager">     <ref bean="jtaTransactionManager"/>   </property>   <property name="lobHandler">     <ref bean="lobHandler"/>   </property>   <property name="mappingLocations">     <value>/WEB-INF/petclinic.hbm.xml</value>   </property>   <property name="hibernateProperties">     <props>       <prop key="hibernate.dialect">net.sf.hibernate.dialect.OracleDialect</prop>     </props>   </property> </bean>

As with the synchronization of Hibernate Sessions, UserTypes will automatically adapt to any kind of transaction that they encounter: If Spring's transaction synchronization is active, it will be used; otherwise, direct JTA synchronization will apply.

Hibernate: Summary

Hibernate is a full-fledged O/R mapping tool. Its excellent query facilities are intuitive and convenient. Its lenient lifecycle requirements for persistent objects make it particularly suitable for working with detached objects: in particular, for use in web applications. Portability across databases is impressive: Specifying the correct database dialect is usually all that is necessary.

Hibernate is particularly well-suited for complex domain models with many associations, where convenient querying and automatic change detection show their full benefits. It does incur a certain level of complexity, however, because of the semantics of a Hibernate Session: for example, on reassociation of a persistent object with a new transaction. For lazy loading, the Session lifecycle needs to be regarded too, for example, through applying Spring's OpenSessionInViewFilter in a web environment.

Hibernate was the first third-party persistence tool supported in Spring; the Hibernate support has been heavily battle-tested and refined. The main facilities that Spring offers are:

  • org.springframework.orm.hibernate.HibernateTemplate: A data access class used in DAOs, seamlessly handling Hibernate Sessions in the background. Automatically participates in both Spring-driven and EJB-driven transactions. Converts the checked HibernateException into Spring's generic DataAccessException hierarchy.

  • org.springframework.orm.hibernate.LocalSessionFactoryBean: A convenient way of setting up a local Hibernate SessionFactory in a Spring context. SessionFactory references can then be passed to DAOs, typically via Spring bean references.

  • org.springframework.orm.hibernate.HibernateTransactionManager: A local transaction strategy, executing transactions on a single target SessionFactory. This strategy works in any environment, in contrast to JtaTransactionManager, which depends on a J2EE server (or a standalone JTA implementation).

  • org.springframework.orm.hibernate.support.HibernateDaoSupport: A convenient base class for DAO implementations, taking a SessionFactory reference and providing a HibernateTemplate instance for it.

  • org.springframework.orm.hibernate.support.OpenSessionInViewFilter and org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor: A Servlet 2.3 Filter and a HandlerInterceptor for Spring's web MVC framework, respectively, providing the Open Session in View pattern for Hibernate. Supports two different modes: single session mode and deferred close mode.

  • org.springframework.orm.hibernate.support.ClobStringType, org.springframework.orm.hibernate.support.BlobByteArrayType, and org.springframework.orm.hibernate.support.BlobSerializableType: Hibernate UserType implementations that delegate to Spring's LobHandler abstraction. Allow for mapping LOB values to properties of persistent classes, even when database-specific LOB handling is necessary (for example, on Oracle 9i).

Mixing Hibernate access code with other data access strategies is as straightforward as possible, as long as all of them access the same JDBC DataSource. Spring's HibernateTransactionManager automatically exposes its transactions to JDBC-based access code. Consequently, adding Spring JDBC or iBATIS SQL Maps to the mix is easy: for example, for BLOB/CLOB or stored procedure access, or for read-only reference data.

Important 

Hibernate is an excellent option for full-fledged O/R mapping. Its greatest strengths are the powerful mapping facilities and the SQL-style query language. Its disadvantages are mainly drawbacks of full-fledged O/R mapping in general: the complexity of automatic change detection, and stricter requirements for lazy loading. Furthermore, it is not ideally suited for legacy data models or over-normalized data models: Spring JDBC and iBATIS SQL Maps are usually better options in such scenarios.

There are many compelling benefits in using Hibernate in conjunction with Spring, rather than alone. Three major benefits are the elimination of the need for custom resource management code like implementation of thread local session management; the reduced lock in to the Hibernate API, which results from placing Hibernate usage in the context of Spring's overall data access abstraction; and increased overall architectural clarity. If you've used Hibernate extensively in the past, you will probably have noticed these and other benefits already.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net