Let's get started covering Hibernate 5 in the Spring environment, using it to demonstrate Spring's approach to integrating OR mappers. This section covers many issues in detail and shows various options for implementing a DAO and demarcating transactions. Most of these patterns can be directly transferred to all other supported ORM tools. Subsequent sections of this chapter describe other ORM technologies and provide brief examples.

Starting with Spring Framework 5.3, Spring requires Hibernate ORM 5.2+ to HibernateJpaVendorAdapter from Spring, as well as for the native setup SessionFactory from Hibernate. It is highly recommended to use Hibernate ORM 5.4 for applications that are just getting started. To use with HibernateJpaVendorAdapter, Hibernate Search needs to be updated to version 5.11.6.

Configuring SessionFactory in a Spring container

To avoid To bind application objects to hard-coded resource lookups, you can define resources (such as DataSource from JDBC or SessionFactory from Hibernate) as beans in a Spring container. Application objects that need access to resources will receive references to such predefined instances through bean references, as shown in the DAO definition in next section.

The following extract from the XML application context definition shows how to set DataSource from JDBC and SessionFactory from Hibernate on top of it:


<beans>
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
        <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
        <property name="username" value="sa"/>
        <property name="password" value=""/>
    </bean>
    <bean id="mySessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="mappingResources">
            <list>
                <value>product.hbm.xml</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <value>
                hibernate.dialect=org.hibernate.dialect.HSQLDialect
            </value>
        </property>
    </bean>
</beans>
        

Moving from local BasicDataSource from Jakarta Commons DBCP to DataSource located in JNDI (usually managed by the application server), is just a matter of configuration, as shown in the following example:


<beans>
    <jee:jndi-lookup id="myDataSource" jndi-name="java:comp/env/jdbc/myds"/>
</beans>

You can also access the SessionFactory located in JNDI using JndiObjectFactoryBean / <jee:jndi-lookup> from Spring to receive and open it. However, this is generally not a typical solution outside the context of an EJB.

Spring also provides a LocalSessionFactoryBuilder option that is easy to interoperate with configuration based on the @Bean annotation and programmatic installation (without the participation of FactoryBean).

Both LocalSessionFactoryBean and LocalSessionFactoryBuilder support background bootstrapping, where Hibernate initialization occurs in parallel with the application boot thread for a given boot executor (for example, SimpleAsyncTaskExecutor). For LocalSessionFactoryBean this is available through the bootstrapExecutor property. The programmatic LocalSessionFactoryBuilder has an overloaded buildSessionFactory method that takes a bootstrap executor argument.

Starting with Spring Framework 5.1, this native Hibernate setup can also expose EntityManagerFactory from JPA for typical JPA interaction along with native Hibernate access.

DAO implementation based on the simple Hibernate API

Hibernate has a feature called contextual sessions, in which Hibernate itself manages one current Session for each transaction. This is roughly equivalent to synchronizing one Session from Hibernate for each transaction in Spring. The corresponding DAO implementation is similar to the following example, based on the regular Hibernate API:

Java

public class ProductDaoImpl implements ProductDao {
    private SessionFactory sessionFactory;
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }
    public Collection loadProductsByCategory(String category) {
        return this.sessionFactory.getCurrentSession()
                .createQuery("from test.Product product where product.category=?")
                .setParameter(0, category)
                .list();
    }
}
Kotlin

class ProductDaoImpl(private val sessionFactory: SessionFactory) : ProductDao {
    fun loadProductsByCategory(category: String): Collection<*> {
        return sessionFactory.currentSession
                .createQuery("from test.Product product where product.category=?")
                .setParameter(0, category)
                .list()
    }
}

This style is similar to the style in the Hibernate reference documentation and examples, except that the SessionFactory is stored in an instance variable. We strongly recommend using this instance-based setup rather than the good old HibernateUtil class from the CaveatEmptor Hibernate sample application. (In general, you should not store resources in static variables unless absolutely necessary.)

The previous DAO example follows the dependency injection pattern. It fits nicely into a Spring IoC container, just as it would if it were written based on the HibernateTemplate template from Spring. It is also possible to set up such a DAO in regular Java (for example, in unit tests). To do this, create an instance of it and call setSessionFactory(..) with the desired reference to the factory. As a Spring bean definition, the DAO object would look like this:


<beans>
    <bean id="myProductDao" class="product.ProductDaoImpl">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>
</beans>

The main advantage of this style of DAO is that it depends only on the Hibernate API. There is no need to import any Spring class. This is attractive from a non-invasive perspective and may seem more natural to Hibernate developers.

However, the DAO throws a HibernateException (which is unchecked, so it doesn't need to be declared or caught), which means that callers can only deal with those exceptions that are considered mostly critical if they want to avoid dependence on Hibernate's own exception hierarchy. It is impossible to determine specific causes (such as optimistic lock failure) without linking the calling program to the implementation strategy. This trade-off may be acceptable for applications that rely heavily on Hibernate, do not need special exception handling, or both.

Fortunately, Spring's LocalSessionFactoryBean supports the SessionFactory.getCurrentSession() method from Hibernate for any Spring transactional strategy, returning the current transactional Session managed by Spring, even when using the HibernateTransactionManager. The default logic for this method is to return the current Session associated with the current JTA transaction, if any. This logic applies whether you use JtaTransactionManager from Spring, container-managed transactions (CMT transactions) from EJB, or the JTA interface.

In general, you can implement DAO objects based on the normal Hibernate API, but still be able to use them in Spring-managed transactions.

Declarative Transaction Distinction

We recommend using Spring's declarative transaction support, which will allow Replace explicit API calls to delimit transactions in Java code with an AOP transaction interceptor. You can configure this transaction interceptor in a Spring container using Java or XML annotations. This ability to use declarative transactions allows business services to free up repetitive transaction demarcation code and focus it on adding business logic that actually matters to the application.

Before continuing, we strongly recommend reading the section "Declarative Transaction Management" if you have not already done so done.

You can mark the service layer with @Transactional annotations and instruct the Spring container to look up these annotations and provide transactional semantics for these annotated methods. The following example shows how to do this:

Java

public class ProductServiceImpl implements ProductService {
    private ProductDao productDao;
    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }
    @Transactional
    public void increasePriceOfAllProductsInCategory(final String category) {
        List productsToChange = this.productDao.loadProductsByCategory(category);
        // ...
    }
    @Transactional(readOnly = true)
    public List<Product> findAllProducts() {
        return this.productDao.findAllProducts();
    }
}
Kotlin

class ProductServiceImpl(private val productDao: ProductDao) : ProductService {
    @Transactional
    fun increasePriceOfAllProductsInCategory(category: String) {
        val productsToChange = productDao.loadProductsByCategory(category)
        // ...
    }
    @Transactional(readOnly = true)
    fun findAllProducts() = productDao.findAllProducts()
}

In the container, you need to configure the PlatformTransactionManager implementation (as a bean) and the <tx:annotation-driven/> entry, allowing processing of @Transactional at runtime. The following example shows how to do this:

 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- SessionFactory, DataSource, etc. omitted -->
    <bean id="transactionManager"
            class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    <tx:annotation-driven/>
    <bean id="myProductService" class="product.SimpleProductService">
        <property name="productDao" ref="myProductDao"/>
    </bean>
</beans>

Delimiting software transactions

You can decouple transactions at a higher application level, on top of data access services at a lower level levels that cover any number of operations. There are also no restrictions on the implementation of the surrounding business service. It only needs PlatformTransactionManager from Spring. Again, the latter can be obtained from anywhere, but it would be preferable to be in the form of a reference to the bean via the setTransactionManager(..) method. Additionally, productDAO must be set by the setProductDao(..) method. The following couple of snippets show the transaction manager and business service definition in the context of a Spring application and an example implementation of a business method:


<beans>
    <bean id="myTxManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
        <property name="sessionFactory" ref="mySessionFactory"/>
    </bean>
    <bean id="myProductService" class="product.ProductServiceImpl">
        <property name="transactionManager" ref="myTxManager"/>
        <property name="productDao" ref="myProductDao"/>
    </bean>
</beans>
Java

public class ProductServiceImpl implements ProductService {
    private TransactionTemplate transactionTemplate;
    private ProductDao productDao;
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }
    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }
    public void increasePriceOfAllProductsInCategory(final String category) {
        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus status) {
                List productsToChange = this.productDao.loadProductsByCategory(category);
                // raise prices...
            }
        });
    }
}
Kotlin

class ProductServiceImpl(transactionManager: PlatformTransactionManager,
                        private val productDao: ProductDao) : ProductService {
    private val transactionTemplate = TransactionTemplate(transactionManager)
    fun increasePriceOfAllProductsInCategory(category: String) {
        transactionTemplate.execute {
            val productsToChange = productDao.loadProductsByCategory(category)
            // raise prices...
        }
    }
}

TransactionInterceptor from Spring allows any checked exception of the application to be thrown along with the callback code, while TransactionTemplate is limited to unchecked exceptions within the callback. TransactionTemplate triggers a rollback in the event of an unchecked application exception or if the transaction is marked by the application to be rolled back (by setting TransactionStatus). By default, TransactionInterceptor behaves the same, but allows you to configure a rollback policy on a per-method basis.

Transaction Management Strategies

And TransactionTemplate , and the TransactionInterceptor delegate the actual transaction processing to an instance of the PlatformTransactionManager (which could be a HibernateTransactionManager (for one SessionFactory from Hibernate), using ThreadLocal Session which is called "behind the scenes") or JtaTransactionManager (delegating to the container's JTA subsystem) for Hibernate applications. You can even use your own PlatformTransactionManager implementation. Moving from native Hibernate transaction management to JTA (for example, when you have distributed transaction requirements for certain deployments of your application) is just a matter of configuration. You can replace Hibernate's transaction manager with Spring's JTA transaction implementation. Both the transaction demarcation code and the data access code work without modification because they use typed transaction management APIs.

In the case of Hibernate sessions distributed across multiple factories, transactions can be combined with JtaTransactionManager as a transactional strategy with multiple LocalSessionFactoryBean definitions. Each DAO will then receive one specific reference to SessionFactory passed to the corresponding bean property. If all underlying JDBC data sources are transactional and containerized, a business service will be able to decouple transactions across any number of DAOs and any number of session factories without much issue if JtaTransactionManager is used as a strategy.

HibernateTransactionManager and JtaTransactionManager allow you to handle cache gracefully at the JVM level using Hibernate without having to look for a transaction manager in the container or a JCA connector (unless you are using EJB to initiate transactions).

HibernateTransactionManager can export Connection from JDBC-based Hibernate to regular JDBC access code for a specific DataSource. This capability allows high-level decoupling of mixed Hibernate and JDBC data access transactions entirely without JTA, as long as you only access one database. The HibernateTransactionManager automatically opens a Hibernate transaction as a JDBC transaction if you have configured the passed SessionFactory with a DataSource via the dataSource property of the LocalSessionFactoryBean class. Alternatively, you can explicitly set the DataSource for which transactions should be opened via the dataSource property of the HibernateTransactionManager class.

Comparing container-managed resources and locally defined resources

You can switch between a container-managed SessionFactory from JNDI and a locally defined one without changing a single line of application code. The question of whether to store resource definitions in a container or locally in an application largely depends on the transaction strategy used. Compared to a local SessionFactory defined by Spring, manually registering a SessionFactory from JNDI does not provide any benefit. Deploying SessionFactory through the Hibernate JCA connector provides the added benefit of being present in the Java EE server management infrastructure, but has no actual value beyond that.

Spring's transaction support is not container-specific . When configured using any strategy other than JTA, transaction support also works in a standalone or test environment. Spring's support for local single-resource transactions is a lightweight and full-featured alternative to JTA, especially in the typical case of single-database transactions. If you use local stateless EJB session beans to manage transactions, there will be a dependency on both the EJB container and the JTA, even if you access only one database and use only those non-stateful session beans to provide declarative transactions via container-managed transactions. Direct use of JTA programmatically also requires a Java EE environment. JTA includes more than just container dependencies in terms of JTA itself and DataSource instances from JNDI. For non-Spring Hibernate transactions managed by JTA, you must use the JCA connector from Hibernate or additional transactional code from Hibernate with TransactionManagerLookup configured for proper caching at the JVM level.

Spring-managed transactions can work just as well with a locally defined SessionFactory from Hibernate as they can with a local DataSource from JDBC, provided they access the same database. Thus, Spring's JTA transaction strategy should only be used when there are distributed transaction requirements. The JCA connector requires container-specific deployment steps and (obviously) JCA support tools first. This configuration will require more work than if you were deploying a simple web application with local resource definitions and Spring-managed transactions. In addition, an Enterprise Edition of the container is often required if, for example, WebLogic Express is used, which does not provide JCA. A Spring application with local resources and transactions spanning a single database runs in any Java EE web container (no JTA, JCA or EJB) such as Tomcat, Resin or even plain Jetty. You can also easily reuse such an intermediate in desktop applications or test suites.

In general, if you are not using EJB beans, stick to setting up locally SessionFactory and HibernateTransactionManager or JtaTransactionManager from Spring. You'll enjoy all the benefits, including proper JVM-level transaction caching and distributed transactions, without all the inconvenience of deploying containers. JNDI registration of SessionFactory from Hibernate via the JCA connector is only beneficial when used in conjunction with EJB beans.

False application server warnings when using Hibernate

In some JTA environments with a very strict XADataSource implementation (currently some versions of WebLogic Server and WebSphere), if Hibernate is configured without a dispatcher JTA transactions for this environment, false warnings or exceptions may appear in the application server log. These warnings or exceptions indicate that the connection being accessed is no longer valid or the JDBC access is no longer valid, perhaps because a transaction is no longer active. As an example, here is a real exception from WebLogic:

java.sql.SQLException : The transaction is no longer active - status: 'Committed'. No
further JDBC access is allowed within this transaction.

Another common problem is connection leakage after JTA transactions if Hibernate sessions (and possibly underlying JDBC connections) are not properly closed This way.

These problems can be solved by making Hibernate compatible with the JTA transaction manager it synchronizes with (along with Spring). There are two options on how to do this:

  • Pass your JtaTransactionManager bean from Spring to the Hibernate configuration. The easiest way is to provide a bean reference in the jtaTransactionManager property for your LocalSessionFactoryBean bean (see "Configuring Hibernate transactions"). Spring will then make the corresponding JTA strategies available to Hibernate.

  • You can also explicitly configure JTA-related Hibernate properties, specifically "hibernate.transaction.coordinator_class", "hibernate.connection" .handling_mode" and possibly "hibernate.transaction.jta.platform" in "hibernateProperties", for the LocalSessionFactoryBean (see the Hibernate manual for details on these properties).

The remainder of this section describes the sequence of events that occur with and without the fact that Hibernate is compatible with the PlatformTransactionManager from JTA.

If Hibernate was configured without any regard to the JTA transaction manager, when a JTA transaction commits, the following events occur:

  • The JTA transaction is committed.

  • JtaTransactionManager from Spring is synchronized with the JTA transaction, so it is called back via the afterCompletion callback by the JTA transaction manager.

  • Among other actions , this synchronization causes a callback from Spring to Hibernate via Hibernate's afterTransactionCompletion callback (used to clear the Hibernate cache), followed by an explicit close() call to Hibernate session, which will cause Hibernate to attempt a close() JDBC connection.

  • In some environments this call to Connection.close() then causes a warning or error to be raised because the application server no longer considers Connection usable because the transaction has already been committed.

If Hibernate is configured with a JTA transaction manager in mind, the following events occur when a JTA transaction commits:

  • The JTA transaction is ready to commit.

  • JtaTransactionManager from Spring is synchronized with the JTA transaction, so the transaction is called back by the JTA transaction manager's beforeCompletion callback.

  • Spring takes into account that Hibernate itself synchronizes with the JTA transaction and begins to behave differently than in the previous scenario. In particular, it aligns with Hibernate's transactional resource management.

  • The JTA transaction is committed.

  • Hibernate is synchronized with the JTA transaction so that the transaction is called back by the afterCompletion callback by the JTA transaction manager and can properly clear its cache .