Wiring Spring with JNDI in Glassfish is more difficult than I thought.
I come across some common exceptions on which there are tons of discussions online. However, I fail to find helpful answers.
Therefore I feel necessary to write this blog to provide a simple solution.
persistence.xml
<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="jta" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/myderby</jta-data-source> <properties> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.ddl-generation.output-mode" value="database" /> <property name="eclipselink.logging.parameters" value="true" /> <property name="eclipselink.logging.level" value="FINE" /> </properties> </persistence-unit> </persistence>
applicationContext.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:jee="http://www.springframework.org/schema/jee" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <context:component-scan base-package="com.demo.controller" /> <context:component-scan base-package="com.demo.dao" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="jta"/> </bean> <tx:annotation-driven /> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> </beans>
UserDao.java
@Repository("userDao") public class UserDaoImpl implements UserDao { @PersistenceContext protected EntityManager entityManager; @Transactional public void save(User user) { entityManager.persist(user); } }
Deployment is fine. When dao is accessed at runtime, an exception is thrown:
java.lang.IllegalStateException: Exception Description: Cannot use an EntityTransaction while using JTA. at org.eclipse.persistence.internal.jpa.transaction.JTATransactionWrapper.getTransaction(JTATransactionWrapper.java:65) at org.eclipse.persistence.internal.jpa.EntityManagerImpl.getTransaction(EntityManagerImpl.java:1153) at org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:70) at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:332)I change the transaction-type from "JTA" to "RESOURCE_LOCAL" in persistence.xml.
The result is even worse. Deployment is complaining:
SEVERE: Exception while preparing the app : The persistence-context-ref-name [com.demo.dao.UserDaoImpl/entityManager] in module [spring-mvc-jpa-jndi] resolves to a persistence unit called [jta] which is of type RESOURCE_LOCAL. Only persistence units with transaction type JTA can be used as a container managed entity manager. Please verify your application.
I am not surprised to see this exception though. Since this is a Java EE environment, the transaction type should only be JTA. So I change the transaction type back to JTA.
I then try a lot of approaches in vain until I see this article:
http://java.dzone.com/tips/how-use-glassfish-managed-jpa
The core value of the article is this single line:
<tx:jta-transaction-manager/>
I replace my transactionManager bean definition with this line and it works perfectly.
After some investigation, it turns out this line creates the transactionMananger instance from this class: org.springframework.transaction.jta.JtaTransactionManager.
So it also works if I replace this line with:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
Now it is easy to explain why the previous transactionManager causes an exception. Clearly the org.springframework.orm.jpa.JpaTransactionManager is the transaction manager for RESOURCE_LOCAL persistence unit while org.springframework.transaction.jta.JtaTransactionManager is the transaction manager for JTA persistence unit. The latter probably delegates all the hassle stuff such as "beginning transaction","committing" and "closing transaction" to the Java EE container while the former needs to do all this chore.
With only one persistence unit defined, I can further strip the applicationContext.xml down to the following final version.
<context:component-scan base-package="com.demo.controller" /> <context:component-scan base-package="com.demo.dao" /> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"/> <tx:annotation-driven /> <tx:jta-transaction-manager/>I am using Spring 3.0.5.
This comment has been removed by the author.
ReplyDelete