Chapter 14. Integration with other technologies

Table of Contents

Hibernate and Spring
Configuration
Use of the Spring template
Alternative approach (nicer)
Transaction handling
Hibernate and Struts
Optimistic locking
Exception handling

In this chapter you will learn, how to integrate Hibernate into other technologies. The approaches shown, are proved in real world applications. I will name common pitfalls, you may come across, as well.

Hibernate and Spring

Spring is a beautiful framework, to implement the business logic of an application. It can integrate Hibernate in a very elegant way. You can download Spring from the page http://www.springframework.org/. This chapter requires that you have knowledge of Spring, as I will not explain any Spring basics.

The documentation of Spring presents three alternatives. I do not appreciate one of these approaches, therefore I will present two of them slightly adapted.

Both examples make use of Spring version 2 and annotations. Therefore you will need a JDK 5 or newer. If you still have to use Java 1.4, you have to configure transaction handling differently. I will give you some hints, when we speak about that topic.

Configuration

There are three approaches to configure Hibernate, when using Spring:

  • Reference a Hibernate configuration from your Spring configuration
  • Complete configuration of Hibernate inside your Spring configuration
  • a mix of both approaches

I recommend the last approach, because some development environments provide auto completion for configuration settings in the Hibernate configuration file. Inside a Spring configuration file this is of course not supported. Below you can see a picture from the MyEclipse plugin for Eclipse showing auto-completion:

title="Auto completion in MyEclipse"

A configuration in Spring requires a datasource and a special SessionFactory provided by Spring. It is important that we use the SessionFactory provided by Spring, else the integration does not work and you may easily encounter connection leaks.

   <bean id="datasource"
      class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="driverClass" value="org.postgresql.Driver" />
      <property name="jdbcUrl"
         value="jdbc:postgresql://localhost:5432/learninghibernate" />
      <property name="user" value="postgres" />
      <property name="password" value="p" />
      <property name="minPoolSize" value="2" />
      <property name="maxPoolSize" value="4" />
   </bean>
   <bean id="hibernateSessionFactory"
      class=
"org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
      <property name="dataSource" ref="datasource" />
      <property name="configLocation">
         <value>classpath:hibernate.cfg.xml</value>
      </property>
   </bean>

The Hibernate configuration file is quite short and does not include any settings for a database connection, Session handling and transaction.

<hibernate-configuration>
   <session-factory>
      <property name="dialect">
         org.hibernate.dialect.PostgreSQLDialect
      </property>
      <property name="cache.provider_class">
         org.hibernate.cache.EhCacheProvider
      </property>
      <property name="hbm2ddl.auto">none</property>
      <mapping class="de.laliluna.example.domain.Hedgehog" />
      <mapping class="de.laliluna.example.domain.WinterAddress" />
   </session-factory>
</hibernate-configuration>

Important tips

You should not configure your datasource in the Hibernate configuration file. If you still do it, you will encounter problems once your transactions are managed by Spring.

Alternatively, you can configure all settings of Hibernate in the Spring configuration.

   <bean id="datasource"
      class="com.mchange.v2.c3p0.ComboPooledDataSource">
      <property name="driverClass" value="org.postgresql.Driver" />
      <property name="jdbcUrl"
         value="jdbc:postgresql://localhost:5432/learninghibernate" />
      <property name="user" value="postgres" />
      <property name="password" value="p" />
      <property name="minPoolSize" value="2" />
      <property name="maxPoolSize" value="4" />
   </bean>

   <bean id="hibernateSessionFactory"
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
      <property name="dataSource" ref="datasource" />
         <property name="annotatedClasses">
         <list>
         <value>de.laliluna.example.domain.Hedgehog</value>
         <value>de.laliluna.example.domain.WinterAddress</value>
         </list>
         </property>
         <property name="hibernateProperties">
         <value>
         hibernate.hbm2ddl.auto=none
         hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
         hibernate.cache.provider_class=org.hibernate.cache.EhCacheProvider
         </value>
         </property>
   </bean>

My example uses a SessionFactory supporting annotations and XML. If you want to use only XML mappings, you can change the class to org.springframework.orm.hibernate3.LocalSessionFactoryBean.

Why not using our simple Hibernate session factory?

We could consider to use our simple session factory, we created in the first example. In that case, we could select one of the patterns explained in chapter Session Handling Chapter 13, Session and Transaction Handling.

Session session = factory.getCurrentSession();
Transaction tx = null;
try {
   tx = session.beginTransaction();
   // do some work
   tx.commit();

} catch (RuntimeException e) {

      try {
         if (tx != null)
            tx.rollback();
      } catch ( RuntimeException e1) {
         log.error("Transaction roleback not succesful", e1);
      }
      throw e;
}

Spring provides functionality to manage transactions in a comfortable way. Transaction management may even include Hibernate and separate SQL queries. If we use our own session factory, we cannot make use of this functionality.

If you use Spring in your project, I recommend to use the transaction handling of Spring as well. In that case, it is very important that you do not use the Hibernate transaction methods like session.beginTransaction.

Spring would not be able to release database connections or close open transactions.

Use of the Spring template

In this chapter, we will have a closer look at the first approach integrating Spring and Hibernate. The full source code is provided in the project HibernateSpring. A Maven build file is so provided. If you use maven, just type

mvn compile

to download all libraries and source files. If you do not use Maven you can download the Spring framework at

http://www.springframework.org/

The example project makes use of the following libraries:

  • spring.jar
  • aspectjrt.jar
  • spring-aspects.jar
  • aspectjweaver.jar

Spring offers a template for different persistence technologies, which encapsulates the specific implementations and exceptions. Exceptions are wrapped by Spring exceptions. Templates exist for example for the following technologies:

  • Hibernate,
  • JDO,
  • Ibatis,
  • Toplink,
  • JPA,
  • JDBC.

If you use multiple of these technologies, the template is a good approach to achieve a common behaviour and transactional handling.

The first step is to create a Hibernate template. This template requires a Spring-Session-Factory as constructor argument.

HibernateTemplate template = new HibernateTemplate(factory);

The template encapsulates Hibernate technology. The following code example shows the usage. It is easier to read the code from the inside.

return (List<Hedgehog>) template.execute(new HibernateCallback() {

   public Object doInHibernate(Session session)
         throws HibernateException, SQLException {
      return session.createCriteria(Hedgehog.class).list();
   }

});

The new HibernateCallback() creates a anonymous implementation of the interface HibernateCallback. This interface has one method: doInHibernate. It is called by Spring and gets as parameter a Hibernate session. The code – doing our work – is in the single line return session.createCriteria(Hedgehog.class).list() .

This freshly created implementation is passed to the execute method of the template in order to run it.

This construction might look like a little bit unusual, but allows Spring to encapsulate the execution of the Hibernate code, to translate exceptions and to role back a transaction if an exception happens. So far, we had to do this on our own.

You might question, which transaction can be rolled back? We haven’t started one. Well, if we do not configure a transaction, Spring will set the transaction mode of the JDBC connection to auto-commit. One of the next chapter will explain how to do make use of transactions explicitly.

A hint: The Hibernate template provides convenient methods for many methods of the Hibernate session: save, saveOrUpdate, delete, update, etc. There is no problem in using them.

template.save(hedgehog);

Problematic methods of the Hibernate template

I recommend not to use the find methods of the Hibernate template. The template has global settings for maximum results, caching behaviour and many others and you can easily get unexpected results, if you forget to remove settings.

I recommend to implement queries with the approach, I have just explained with the HibernateCallBack and only to use template methods for simple methods like save, update etc.

Alternative approach (nicer)

There is probably nicer code than the template with a CallBack implementation. Therefore I want to show an approach not using this construct as well. The example project is called HibernateSpring2.

We will make use of the method getCurrentSession which exist in the Spring-Hibernate Session-Factory as well. Like our examples with a current session context we just call getCurrentSession to access the Hibernate session.

protected Session getCurrentSession() {
   return factory.getCurrentSession();
}
public void save(Hedgehog object) {
   getCurrentSession().saveOrUpdate(object);
}

This code is not yet complete. With this approach we must manage transactions explicitly and start one before we call getCurrentSession.

Exception handling

The Spring documentation asserts that with this approach, we have to handle Hibernate exceptions in our code instead of Spring exceptions.

This is not very precisely explained. The exception is translated a little bit later. In the first approach (template) the exception was translated to a Spring exception by the template. The second approach will translate the exception with the transaction handling after our method was processed.

As Hibernate exceptions are fatal RuntimeExceptions and should be handled in the frontend layer – for example a webapplication - , this is no disadvantage at all. We would not deal with a Hibernate exception inside of a business method.

In my opinion the second approach is a better choice because it is better readable and less code to write.

Transaction handling

It is quite simple to manage transaction with Spring. We only have to configure a transaction manager. In this example we will use a JDBC based transaction manager.

<tx:annotation-driven transaction-manager="txManager"/>
<bean id="txManager" class=
"org.springframework.orm.hibernate3.HibernateTransactionManager" >
   <property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>

The spring documentation explains how to configure JTA based transaction managers. JTA configuration is application server specific.

It is quite easy to integrate transactions in our code. We only have to add a @Transactional annotation in front of a method. Spring will start a transaction before the method and commit at the end.

@Transactional(propagation = Propagation.REQUIRED)
public void update(Hedgehog hedgehog) {
   hedgehogDao.update(hedgehog);
}

propagation defines which kind of exception we use.

Table 14.1. Transaction types

Type of transactionDescription

REQUIRED

transaction is required. If one exist it will be used else one will be created. This is the default.

SUPPORTS

transactions are supported but won’t be started, If a transaction exist the method runs in this transaction scope else the method runs without transaction scope.

MANDATORY

transaction is required but won’t be started, If no transaction exist an Exception will be thrown.

REQUIRES_NEW

will always start a new transaction, If one exists already it will be paused. This is not supported by all transaction managers.

NOT_SUPPORTED

method will not inside of a transaction, If one exists already it will be paused. This is not supported by all transaction managers.

NEVER

Transactions are not supported, If a transaction exist an exception will be thrown.

NESTED

A nested transaction will be started. You can use the transaction manager DataSourceTransactionManager to achieve this. Only a limited number of database and JTA transaction manager supports this configuration.


If a RuntimeException happens inside of our method, Spring will rollback the transaction. This will not be done if a checked exception happens. You can configure the rollback behaviour for checked exceptions explicitly. Have a look in the Spring documentation for further informations.

Transaction configuration per class

You can add the @Transactional annotation in front of the class as well. In this case Spring will add transaction handling to all public methods. If a method should use a special kind of transaction, you can overwrite the default with a annotation in front of the method.

Transactions without annotations

In my opinion, the annotation based transaction handling is a very beautiful solution. If you cannot use annotations because you don’t have the required JDK 5, you have to choose another approach.

In this case we would define transactions in the Spring configuration file, start and commit transactions explicitly in the source code or use an transaction template. All approaches are described in the Spring documentation.