'JPA + Spring: UnexpectedRollbackException when Transaction is REQUIRED, REQUIRES_NEW or NESTED
I'm doing my first project with Spring, JPA, and GlassFish 3.1 and I'm having some troubles. I've been looking for a solution for a week, but I have found nothing.
I have an Entity (called Role), a GeneralReposiroty with general methods to access any entity, a RoleRepository which implements particular methods about Role, and a Service class, which calls RoleRepository. Methods of Service class are transactional, but I get RollBackException if the propagation property is REQUIRED, REQUIRES_NEW or NESTED. If propagation is MANDATORY, it's thrown an exception saying:
org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:339)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:101)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
...
If it's NOT_SUPPORTED, NEVER or SUPPORT, select queries work fine, but I get the same RollbackException if I try to execute a create, update or delete query.
I've debugged the select queries, and before they throw the RollBackException, the query gets the result fine, and the exception is thrown just at the end of the function. In adition, if I get the Entities with find() of EntityManager and the service method doesn't have the @Transactional anotation, works fine, but if I make it transactional, it crashes with the same exception.
This happens with all entities I have, but I use that to explain the problem.
I think there is something wrong with my Spring configuration, but I don't know what.
This is the full trace of the exception (In some lines there are references to AuthenticationSuccessHandler class, because it's where I call to the service class from):
WARNING: StandardWrapperValve[default]: PWC1406: Servlet.service() for servlet default threw exception
org.springframework.transaction.UnexpectedRollbackException: JTA transaction unexpectedly rolled back (maybe due to a timeout); nested exception is javax.transaction.RollbackException: Transaction marked for rollback.
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:845)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:314)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:116)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
at com.citius.reservas.service.service$$EnhancerByCGLIB$$37b50d4.tryJPAFind(<generated>)
at com.citius.reservas.AuthenticationSuccessHandlerImpl.onAuthenticationSuccess(AuthenticationSuccessHandlerImpl.java:47)
...
Caused by: javax.transaction.RollbackException: Transaction marked for rollback.
at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:428)
at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:855)
at com.sun.enterprise.transaction.UserTransactionImpl.commit(UserTransactionImpl.java:208)
at org.springframework.transaction.jta.JtaTransactionManager.doCommit(JtaTransactionManager.java:842)
... 45 more
I have the following entity:
@Entity
@Table(name = "roles", catalog = "reservas")
@XmlRootElement
public class Role implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@NotNull
@Size(min = 1, max = 50)
private String name;
private static final long serialVersionUID = 1L;
GenericRepository, a general class, which is implemented by all the repositories:
public class GenericRepositoryImpl<T> implements GenericRepository<T> {
@PersistenceContext
private EntityManager em;
private Class<T> type;
public GenericRepositoryImpl( ){
Type t = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) t;
type = (Class) pt.getActualTypeArguments()[0];
}
@Override
public T find(Object pk) {
try{
return em.find(type, pk);
}catch(java.util.NoSuchElementException ex){
return null;
}
}
@Override
public T create(T t) {
em.persist(t);
em.flush();
em.refresh(t);
return t;
}
@Override
public List<T> query(Query q) {
List<T> list= q.getResultList();
if(list==null)
list=new ArrayList<T>();
return list;
}
}
This is the role repository:
@Repository
public class RoleRepositoryImpl extends GenericRepositoryImpl<Role>
implements RoleRepository{
@PersistenceContext
private EntityManager em;
@Override
public Role findByName(String name) {
Query q = (Query) this.em.createNamedQuery("Role.findByName");
q.setParameter("name", name);
Role r = (Role) q.getSingleResult();
return r;
}
@Override
public List<Role> findAll() {
Query q = (Query) this.em.createNamedQuery("Role.findAll");
return this.query(q);
}
}
The Service class:
@Service
public class service {
@Autowired
private RoleRepository roleRepository;
@Transactional
//This throws a RollBackException
public void tryJPAEmpty(){
roleRepository.findByName("example");
}
//This doesn't
public Role tryJPAFind(){
return roleRepository.find(new Integer(1));
}
}
Spring configuration (applicationContext.xml) is:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
">
<context:component-scan base-package="com.***.***" />
<jpa:repositories base-package="com.***.***.repositories" />
<tx:annotation-driven />
<tx:annotation-driven transaction-manager="transactionManager" />
<context:annotation-config />
<beans>
<bean id="EntityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="JPAReservas"/>
</bean>
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager" >
<property name="allowCustomIsolationLevels">
<value>true</value>
</property>
</bean>
</beans>
</beans>
Persistence.xml (I have a data source and a connection pool created in my GlassFish server):
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" 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">
<persistence-unit name="JPAReservas" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>Reservas_mysql</jta-data-source>
<!--Named queries-->
<mapping-file>META-INF/mapping/Role.xml</mapping-file>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
</persistence-unit>
</persistence>
And finally, I have named queires in a xml file, like that: SELECT r FROM Role r
<named-query name="Role.findByName">
<query>SELECT r FROM Role r WHERE r.name = :name</query>
</named-query>
I'm really noob with Spring and JPA, so if you know some improvements, or changes I can do, please tell me.
Solution 1:[1]
Ok, I've solved it changing transaction-type to RESOURCE_LOCAL, but at the beggining, Glassfish gives me a lot of troubles. That's the way I solved it:
I've add metadata-complete="true" to the header of web.xml. Here is explained why is this necesary:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0" metadata-complete="true">
I've changed my persistence.xml, because with RESOURCE_LOCAL the application won't use the connection pool of Glassfish, I have to put information about database connection:
<persistence-unit name="JPAReservas" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<mapping-file>META-INF/mapping/User.xml</mapping-file>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/reservas"/>
<property name="javax.persistence.jdbc.user" value="admin"/>
<property name="javax.persistence.jdbc.password" value="adminpassword"/>
</properties>
</persistence-unit>
And at the end, I've changed the applicationContext.xml (Spring configuration), because I can't use the same EntityManager class with RESOURCE_LOCAL as with JTA:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="EntityManagerFactory" />
</bean>
And that's all. I don't know why I can't use JTA like I was doing before, but I've solved it!!! :). Thank you!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Frostia Feijoo |
