Engineering
Releases
News and Events
Using JPA in Spring without referencing Spring

Spring 2.0 has added support for the JPA data access standard with all of the standard Spring support classes one would expect. Mark Fisher has a great post on how to use this new support. However one of the questions that we keep getting is why one would want to use a Spring class (JpaTemplate) to access an EntityManager. The best answer for this question lies in the value add that JpaTemplate provides. In addition to providing the one-liner convenience methods that are a hallmark of Spring data access, it also provides automatic participation in transactions and translation from PersistenceException to the Spring DataAccessException hierarchy.

But I still don't want to use JpaTemplate

That's fine because you don't have to sacrifice the power of Spring. Specifically the two biggest advantages (transaction participation and exception translation) are available without coding against Spring classes. In fact Spring actually has extensive support for plain API DAOs.

Transaction Participation

One of the benefits of Spring's declarative transaction management is that you never have to reference transaction structures in your code. So if you want automatic transaction participation all you need are a couple of bean definitions.

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" />

<bean class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven />

The JpaTransactionManager is responsible for creating EntityManagers opening transactions and binding them to the current thread context. The <tx:annotation-driven /> simply tells Spring to put transactional advice on any class or method that has an @Transactional annotation on it. You can now just write your main-line DAO logic without having to worry about transactional semantics.

public Collection loadProductsByCategory(String category) {
    return entityManager.createQuery("from Product p where p.category = :category")
        .setParameter("category", category).getResultList();
}

Exception Translation

If you want Spring's exception translation you can get that as well. All that needs to happen is the introduction of the @Repository annotation on your class. This (really minor) Spring annotation simply tells the Spring container that this class is a persistence repository and needs to have exception translation performed on it. To get the exception translation a simple bean definition is required.

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

That's fine, but how do I get an EnityManager?

This might actually be the coolest part. Basically you'd just define a DAO exactly the way you would if you weren't using Spring by adding the @PersistenceContext JPA annotation.

public class ProductDaoImpl implements ProductDao {

    private EntityManager entityManager;

    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this. entityManager = entityManager;
    }

    public Collection loadProductsByCategory(String category) {
        return entityManager.createQuery("from Product p where p.category = :category")
            .setParameter("category", category).getResultList();
    }
}

By adding a single bean definition the Spring container will act as a JPA container and inject an EnitityManager from your EntityManagerFactory.

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

A post this long must mean a lot of code and configuration

But it doesn't! Now that we've show all the pieces and parts let's take a look at the complete system.

Code

  • @Repository for exception translation
  • @PersistenceContext for EntityManager injection
  • Plain JPA API code!
@Repository
public class ProductDaoImpl implements ProductDao {

    private EntityManager entityManager;

    @PersistenceContext
    public void setEntityManager(EntityManager entityManager) {
        this. entityManager = entityManager;
    }

    public Collection loadProductsByCategory(String category) {
        return entityManager.createQuery("from Product p where p.category = :category")
            .setParameter("category", category).getResultList();
    }
}

Configuration

  • LocalEnityManagerFactoryBean to create the EntityManagerFactory
  • JpaTransactionManager to manager JPA transactions
  • <tx:annotation-driven /> to tell Spring to look for @Transactional
  • Your bean definition!
<?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:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx.xsd">

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean" />

    <bean id="productDaoImpl" class="product.ProductDaoImpl"/>

    <bean
        class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <bean class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory"
            ref="entityManagerFactory" />
    </bean>

    <tx:annotation-driven />

</beans>

That's it. Two annotations and four bean definitions.

Additional Resources



Updated to remove ellipses from bean definitions. See comments for background.
comments powered by Disqus