From Hibernate to JPA

By timothynjones

So, I’m using Hibernate 3.2 with annotations for the persistence layer of my investment application. I have some generated values in certain domain objects that are fairly expensive to compute (such as the internal rate of return of an account), and so I don’t want them recomputed every time the getter is called. EJB3 defines a @PostLoad annotation which specifies a method to be invoked after a domain object is loaded from the store. Perfect. The only problem is that it doesn’t work using SessionFactory-based configuration, you have to use an EntityManager-based configuration or the post-load method never gets called (i.e., it silently fails – nice caveat of annotations, that).

Anyway, I’ve converted my Spring configuration to use JPA, which is pretty nice since it (almost) makes the configuration Hibernate-agnostic (I say “almost” because you still have to specify the jpaVendorAdapter on the EntityManagerFactory).

Here’s my persistence.xml file, which I keep down to the bare minimum by configuring the data source at the Spring level (so I can use different Spring contexts and data sources for testing):

<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://java.sun.com/xml/ns/persistence"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"

version="1.0">  <persistence-unit name="Invest">

<!-- Uncomment this block to specify the data source at the JPA level rather than the Spring level

<provider>org.hibernate.ejb.HibernatePersistence</provider>

<properties>

<property name="hibernate.dialect"

value="org.hibernate.dialect.MySQLDialect" />

<property name="hibernate.connection.driver_class"

value="com.mysql.jdbc.Driver" />

<property name="hibernate.connection.url"

value="jdbc:mysql://localhost/invest" />

<property name="hibernate.connection.username" value="root" />

<property name="hibernate.connection.password" value="" />

</properties>

-->

</persistence-unit>

</persistence>

BTW, the persistence.xml file has to be located in the META-INF directory of one of the classpath entries. I use an Ant target copy-resources to copy this to build/classes/META-INF from my conf directory.

Here’s an exerpt from my Spring context file:

<?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="dataSource" class="org.apache.commons.dbcp.BasicDataSource"

destroy-method="close">

<property name="driverClassName" value="com.mysql.jdbc.Driver" />

<property name="url" value="jdbc:mysql://localhost/invest" />

<property name="username" value="root" />

<property name="password" value="" />

</bean>

<bean id="securityDao"

class="com.jolai.invest.dao.jpa.JpaSecurityDao">

<property name="entityManagerFactory" ref="entityManagerFactory" />

</bean>

<bean id="accountDao"

class="com.jolai.invest.dao.jpa.JpaAccountDao">

<property name="entityManagerFactory" ref="entityManagerFactory" />

</bean>

<bean id="portfolioDao"

class="com.jolai.invest.dao.jpa.JpaPortfolioDao">

<property name="entityManagerFactory" ref="entityManagerFactory" />

</bean>

<bean id="entityManagerFactory"

class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="persistenceUnitName" value="Invest" />

<property name="dataSource" ref="dataSource" />

<property name="jpaVendorAdapter">

<bean

class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

</property>

</bean>

<bean id="transactionManager"

class="org.springframework.orm.jpa.JpaTransactionManager">

<property name="entityManagerFactory" ref="entityManagerFactory" />

<property name="dataSource" ref="dataSource" />

</bean>

</beans>

Note that DAOs should extend JpaDaoSupport instead of HibernateDaoSupport, and unit tests should extend AbstractTransactionalSpringContextTests instead of AbstractTransactionalDataSourceSpringContextTests.

Everything works, but I did get bit by Hibernate bug EJB-244, which will cause your unit tests to fail with an “unknown entity” exception if you’re running them from a directory whose full path contains a space (like “My Documents” under Windows). Arrgh. I used the junction utility to link /home/tjones to my “My Documents” directory.

I like this approach because JPA should be a bit more standards-compliant and allow me to swap out Hibernate for TopLink or iBatis with no code changes (in theory).

Here are some links which I found helpful during this exercise:

5 Responses to “From Hibernate to JPA”

  1. Keith Says:

    I read it and it worked for me! Thanks a bunch.

  2. satish aytha Says:

    I am trying to develop some DAOs using hibernate, spring and JPA but I am getting some jboss related classes not found exception. I have included spring 3.2 core and entity manager jars in web classpath. what all other jar files are required?

  3. Raj Says:

    Hello Satish:
    Here is the list of the files you will need for the hibernate, JPA. I am not yet using Spring and plan to do so.
    antlr-2.7.6.jar
    antlr.license.txt
    aopalliance-1.0.jar
    asm-attrs.jar
    asm.jar
    c3p0-0.9.1.jar
    cglib-2.1.3.jar
    ehcache-1.2.3.jar
    hibernate-3.2.5.ga.jar
    hibernate-annotations-3.2.1.ga.jar
    hibernate-entitymanager-3.2.1.ga.jar
    javassist-3.3.ga.jar
    jboss-archive-browsing-5.0.0alpha-200607201-119.jar
    jdbc2_0-stdext.jar
    jdbc2_0-stdext.licence.txt
    jta.jar
    jta.licence.txt
    persistence-api-1.0.jar

    I hope that helps.

  4. Narender Says:

    nice article,

    thanks

  5. Dulce Says:

    I am trying to integrate hibernate, spring structs and jpa but I have a lof of problem. I follow a example from: http://cwiki.apache.org/S2WIKI/struts-2-spring-2-jpa-ajax.html

    Could you help me?.The error is:

    18/04/2008 04:55:33 PM org.apache.catalina.core.StandardContext listenerStart
    GRAVE: Excepción enviando evento inicializado de contexto a instancia de escuchador de clase org.springframework.web.context.ContextLoaderListener
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor’ defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘mySessionFactory’ defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: net/sf/ehcache/CacheException
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘mySessionFactory’ defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: net/sf/ehcache/CacheException
    Caused by: java.lang.NoClassDefFoundError: net/sf/ehcache/CacheException
    at java.lang.Class.getDeclaredConstructors0(Native Method)

Leave a Reply