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:
March 6, 2007 at 5:53 pm |
I read it and it worked for me! Thanks a bunch.
September 26, 2007 at 7:05 am |
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?
December 13, 2007 at 10:46 am |
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.
February 8, 2008 at 7:48 am |
nice article,
thanks
April 18, 2008 at 2:05 pm |
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)