Using JPA in Spring without referencing Spring

Ben Hale

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.

 

23 responses


  1. Couple of issues:

    1) JpaTemplate, for inexplicable reasons, slows down my app startup by an order of magnitude. So that's another reason to avoid it.

    2) Spring's JPA support is pretty bad at handling multiple persistence units (multiple persistence.xml files). The implementations will always scan everything and pick the right one.

    I'm sure both of the two issues above will be addressed in time, but it's something to be wary of.

    Also instead of using the Spring specific tx:annotation-driven, pitchfork will allow you to use spec compliant @TransactionAttribute annotations instead.

    Finally (and this has always caused me so much heartache) why does EVERY Spring xml example contain the ubiquitous '…' section (in the entity manager bean declaration)? It's often that bit that causes the most heartache!


  2. You're absolutely right on the use of ellipses in bean definition files. Here I am trying to prove that it doesn't take much configuration and I skipped the largest part. In my defense I skipped it because the configuration was so implementation dependent, not because of it's size. I've updated the post to remove the ellipses and highlight one of the niceties of Spring's LocalEntityManagerFactoryBean. By default it will use the Persistence class to find the JPA implementation and use the default EntityManager configuration so in the trivial case no configuration is actually required.


  3. After a bit of poking around I can now follow up on some of your concerns. A quick look at JpaTemplate implies that it\'s a pretty simple bean that doesn\'t have any complex startup routines. It\'s possible however that the slowdown is tied to your second point. It turns out that the behavior to search the entire classpath is only the default behavior in the LocalContainerEntiyManagerFactoryBean. And you can simply override that search by putting in a value for persistenceXmlLocaion. This behavior is actually the spec\'d behavior for all JPA providers. At any rate, you may simply be experiencing the same kind of slowdowns that people perceive when the use Hibernate; a slowdown from the technology, not the framework. If you have some profiling data or an example that I could run a profiler on, I\'d be happy to see what\'s up for you.

    As to the use of @TransactionAttribute, that\'s a more difficult call. There is much to be said for using the standard JEE annotations, but there are some downsides. The first is that @TransactionAttribute only allows you to specify propagation rules currently. There is no way to specify rollback or isolation rules. There is an @TransactionException annotation that can help with rollback behaviors but there are some serious problems with it. The issues are a little more involved than a comment should be, so I\'m trying to convince Juergen to write a blog entry on it.

    Needless to say, Spring will continue to support JPA transaction annotations as part of Pitchfork and may at some point honor them in the base transaction support. But for the foreseeable future we still feel that @Transactional provides value-add beyond JPA.


  4. Hi Ben,

    Regarding the classpath scanning, the issue isn't with the spec, it's with Spring's implementation (at least partially). it's sort of a design issue. Instead of having to specify an EMF Bean for every persistence unit, a saner approach would have been to define a bean that houses all the EMF's in the classpath. So in that case, it'd suck in all the persistence.xml, and LocalEMFBean for each one of the found resources. I agree that I'd still be paying the hibernate cost, but at least Spring wouldn't be doing its bit to increase the cost even more.

    Regarding transactions, I never claimed that @TransactionAttibute is better than @Transactional, I'm quite aware that Spring's transaction handling is more powerful. The point is that @Transactional IS referencing Spring, and using a spring specific feature. I'm sure we're all familiar with vendor documentatin for specs always suggesting that the user use a vendor specific feature, and I'm just pointing out that that's exactly what you're doing here, in an article that has a title implying the opposite!


  5. Nice short post for a taste of how to use JPA in Spring. I would recommend the LocalContainerEntityManagerFactoryBean, though, as opposed to the vanilla Local variety. The Local one is not really acting like a JPA Container like the LocalContainer one does.

    Also, isn't the default persistence provider the TopLink Essentials RI?


  6. For anything beyond the simplest case, LocalContainerEntityManagerFactoryBean is definitely the preferred class.


  7. Has somebody succeeded to run this example of Spring 2.0 JPA?
    Seems that PersistenceAnnotationBeanPostProcessor is not called, so the EntityManager dependency is not injected and I get NullPointer exception…

    Can somebody upload a complete (and working!) ZIP archive with Java and XML files for this sample? Maybe I'm doing wrong something.

    Thanks


  8. I have resolved the problem - thanks to gschmutz
    http://forum.springframework.org/showthread.php?p=75109


  9. [quote comment="42"]Hi Ben,

    Regarding the classpath scanning, the issue isn't with the spec, it's with Spring's implementation (at least partially). it's sort of a design issue. Instead of having to specify an EMF Bean for every persistence unit, a saner approach would have been to define a bean that houses all the EMF's in the classpath. So in that case, it'd suck in all the persistence.xml, and LocalEMFBean for each one of …[/quote]
    JIRA issue here (http://opensource.atlassian.com/projects/spring/browse/SPR-2531) - likely to be addressed in the upcoming 2.0-RC4.


  10. I am not sure if I am missing something here. I keep getting the following exception:

    org.springframework.dao.InvalidDataAccessApiUsageException: Unknown entity bean class: class com.volvo.ewarranty.domain.ModelFamily, please verify that this class has been marked with the @Entity annotation.

    persistence.xml contains the classes. The class has been annotated. My applicationContext is setup as Ben described here in this post.

    I am attempting to connect to a Derby db.

    Any thoughts.


  11. Attn: Dan Frey

    I've been experiencing the exact same problem, and I'm pretty sure I've followed the Spring JPA tutorial to a tee. Have you had any luck fixing your problem?


  12. Attn: Yogindra

    I believe it had something to do with Derby. I installed Oracle 10g XE and modified my connection settings and the problem disappeared. I haven't had the time to go back and further research why Derby was failing. Also, I made sure that I was explicitly defining the items in the persistence.xml. For example, the classes.

    Sorry I couldn't be of more help.

    Dan


  13. I had to give the bean a name "transactionManager" to get this to work. Here is my working configuration.


  14. It trimmed my xml config from my post. Here is an html encoded xml.

    <?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.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

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

    <tx:annotation-driven />
    </beans>


  15. A comment and a question:

    First, thank you for this post. Nice to see all of this put together with the examples.

    My comment: I struggled with the same issue that vatel did regarding beanFactory v.s. ApplicationContext. I think it is worth noting in your presentation that post processors like the PersistenceAnnotationBeanPostProcessor only get called if beans are loaded using the ApplicationContext. I'm fairly new to spring, so maybe this is commonly understood, and I missed it.

    My Question: I am implementing a generic DAO implementation in which all of my DAO's inherit from a common base class (GenericDAO). I would like to keep the EntityManager in the base class to be used by all subclasses. I have tried implementing this with the @PersistenContext annotation in the base class, however, PersistenceAnnotationBeanPostProcessor does not seem to inject the entity manager to the base class. If I create a subclass attribute/function and annotate this, then the injection works fine. Do I have a configuration issue, or is this a limitation in spring?

    Any input would be greatly appreciated.


  16. Disregard last question: found visibility on base class function was not set correctly. All is working well.

    Thank you


  17. I have implemented this in a project but am running into a strange issue. Everything works great until I try and delete. When I try that I get the following exception:

    java.lang.IllegalArgumentException: Removing a detached instance com.jc.dao.entity.Story#1
    at org.hibernate.ejb.event.EJB3DeleteEventListener.performDetachedEntityDeletionCheck(EJB3DeleteEventListener.java:47)
    at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:75)
    at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:49)
    at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:766)
    at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:744)
    at org.hibernate.ejb.AbstractEntityManagerImpl.remove(AbstractEntityManagerImpl.java:245)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:176)
    at $Proxy15.remove(Unknown Source)

    For frame of reference I'm using a generic DAO similar to an early poster. This generic DAO has all the basic CRUD operations. Add, Update and Retrieve all work just fine, it's simply the delete that fails.

    I've searched and it seem that this has something to do w/ my transactions though I can't (though experimentation) track it down. I was wondering if anyone else has see this behavior and found a resolution.

    Thanks in advance for any help.


  18. I think you should provide a downloadable working examaples in your articles. The theory is ok but when we try what you suggest IT DOES NOT WORK because there is alway some piece missing or incomplete!


  19. Have you tried this with Glassfish V2? I get the following error:
    javax.el.ELException: org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: Exception Description: Cannot use an EntityTransaction while using JTA.
    I think the problem is that the JpaTransactionManager does not use JTA ?


  20. I wish I could delete my previous comment.
    My code is working fine now based on what explained in this article. I had to struggle with the configuration a little but I have gained better understanding of the Spring framework. Thank you


  21. The problems with loading multiple persistence units are addressed in Spring 2.5 by the PersistenceUnitManager check out the documentation on this, for the full story. http://static.springframework.org/spring/docs/2.5.x/reference/orm.html#orm-jpa.


  22. Hi,

    is the solution with @PersistenceContext demonstrated above thread-safe? As far as I know in Spring a bean is a singleton per default. What happens if two or more threads call methods of the injected entityManager in parallel?

    Thanks.
    Martin


  23. Yes, this is thread-safe, since the injected EntityManager is actually a shared EntityManager proxy that will transparently delegate to the actual thread-bound EntityManager resource in the background.

    In other words, any "xxx" call on that EntityManager proxy actually translates to a sort of "entityManagerFactory.getCurrentEntityManager().xxx" call at runtime.

    This style is actually defined by the JPA specification; we just adapted it into the Spring environment.

    Juergen

3 trackbacks

Leave a Reply