Spring 3, Hibernate JPA, Tomcat transactions
I tried to deploy a webapp that uses Spring 3, JPA with the Hibernate implementation and Tomcat. There are domain classes with a DAO layer which is called from the Service layer. The classes are annotated correctly. All of the unit tests work. Actually the whole webapp works deployed on other web containers : but not on Tomcat!
The problem was that in my service’s save() method I required a transaction. Of course! That’s what you have to do. The actual problem is that I needed a transaction in that method but one was not being created.
1 2 3 4 5 |
@Override @Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class, readOnly = false) public Composer create(Composer composer) { return this.composerDAO.create(composer); } |
In the spring config files I did have the usual wiring:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<import resource="dataSource.xml"/> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:dataSource-ref="dataSource" p:entityManagerFactory-ref="entityManagerFactory"> <property name="jpaDialect" ref="jpaDialect" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" p:dataSource-ref="dataSource" p:jpaDialect-ref="jpaDialect" p:persistence-xml-location="classpath*:${persistence.xml}" p:persistenceUnitName="ComposerJPA"> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" p:showSql="${jpaVendorAdapter.showSql}" p:generateDdl="${jpaVendorAdapter.generateDdl}" p:databasePlatform="${hibernate.dialect}" p:database="${jpaVendorAdapter.database}"/> </property> </bean> <!-- used by tx manager and entityManagerFactory --> <bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> <context:annotation-config /> <context:property-placeholder location="/WEB-INF/spring/db.properties" /> |
I know you’ve seen this all before.
It helps to modify your logging level for Spring’s transaction classes. Here’s an excerpt from my log4j.properties.
1 2 3 4 |
log4j.logger.org.springframework.orm.jpa.JpaTransactionManager=DEBUG log4j.logger.org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter=DEBUG log4j.logger.org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean=DEBUG log4j.logger.org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor=DEBUG |
You see that my config did include the tx:annotation-driven element which creates transaction proxies for methods annotated with @Transactional.
1 |
<tx:annotation-driven transaction-manager="transactionManager" /> |
One thing I modified was the transaction manager’s mode.
1 |
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj"/> |
Another part of the problem is Tomcat’s classloader. You need to specify a special classloader. I created a context.xml in my webapp directly (you can create it in various other Tomcat locations).
So, I created a file named src/main/resources/META-INF/context.xml
1 2 3 4 |
<Context path="/ConcertMVC" reloadable="false"> <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/> </Context> |
Since I was using the SpringSource Tools Suite which includes Tomcat that was it. If you are using a Tomcat distribution from Apache, you need to add a Spring jar to you library directory. This jar is included in the Spring download. If you use Maven then you can add the following dependency:
1 2 3 4 5 |
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument-tomcat</artifactId> <version>${org.springframework-version}</version> </dependency> |
The add it to your tomcat library directory by copying from your Maven repository.