I got this problem for a while and can't figure out what I am doing wrong. Here is the context:
- Spring 4.3.4.RELEASE
- Spring-data-jpa 1.10.6.RELEASE
- Hibernate 5.2.5.Final
- I'm using wildfly 10, starting via maven plugin
- configured datasource for JNDI lookup correctly
- using spring-data with @Repository on interfaces
- created entityManagerFactory as a spring bean (LocalContainerEntityManagerFactoryBean)
- as for transaction manager, I tried to use both 'tx:jta-transaction-manager' and spring bean (JtaTransactionManager)
- configured persistence.xml
- enable 'tx:annotation-driven'
So, I got a @Service class (SistemaBOImpl) that has a @Repository injected (SistemaDAO) and a test method for transactional behavior:
@Service
@Transactional(propagation = Propagation.SUPPORTS)
public class SistemaBOImpl extends AbstractBO<Sistema, Long> implements SistemaBO {
@Autowired
public SistemaBOImpl(SistemaDAO sistemaDAO) {
super(sistemaDAO);
}
@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { BusinessException.class })
public void testTrans() throws BusinessException {
try {
final Sistema s = findOne(1L);
s.setCodUsuAlt(SimpleDateFormat.getInstance().format(new Date()));
s.setDatAlt(new Date());
save(s); //1st save
save(new Sistema()); //2nd save
} catch (Exception e) {
throw new RuntimeException(e);
// throw new BusinessException(e);
}
}
}
DAO
@Repository
@Transactional
public interface SistemaDAO extends DAO<Sistema, Long>, JpaRepository<Sistema, Long> {}
The problem is: Although 2nd save won't complete due to database constraints (and I did it on purpose, 1st save is commited and data is changed. I can't figure out why it is not rolling back all. What am I doing wrong?!?
Here are my other config file:
applicationContext.xml
<beans ... ommited namespace hell>
<context:annotation-config />
<context:component-scan base-package="br.com.myco" />
<tx:jta-transaction-manager />
<tx:annotation-driven transaction-manager="transactionManager" />
<jpa:repositories base-package="br.com.myco" />
<jee:jndi-lookup id="dataSource" jndi-name="java:jboss/datasources/Myco_DB" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="jtaDataSource" ref="dataSource" />
<property name="jpaVendorAdapter" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" >
<property name="database" value="MYSQL" />
<property name="showSql" value="true" />
</bean>
</property>
<property name="persistenceUnitName" value="tof"/>
<property name="packagesToScan" value="br.com.myco.tof"/>
</bean>
persistence.xml
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1">
<persistence-unit name="myco" transaction-type="JTA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/datasources/Myco_DB</jta-data-source>
<class>br.com.abril.tof.domain.Sistema</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
</properties>
</persistence-unit>
web.xml
<web-app version="3.1" ...>
<display-name>MyCO</display-name>
<description>MyCO</description>
<!-- Carrega Spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>myco</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myco</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<persistence-unit-ref>
<persistence-unit-ref-name>jpa/EntityManager</persistence-unit-ref-name>
<persistence-unit-name>myco</persistence-unit-name>
</persistence-unit-ref>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
Log/Stacktrace for 2nd save
Caused by: org.hibernate.HibernateException: Could not apply work
at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.doTheWorkInNewTransaction(JtaIsolationDelegate.java:112)
at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.delegateWork(JtaIsolationDelegate.java:65)
at org.hibernate.id.enhanced.TableStructure$1.getNextValue(TableStructure.java:125)
at org.hibernate.id.enhanced.NoopOptimizer.generate(NoopOptimizer.java:40)
at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:412)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)
at org.hibernate.jpa.event.internal.core.JpaPersistEventListener.saveWithGeneratedId(JpaPersistEventListener.java:67)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:189)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:132)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:1146)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347)
at com.sun.proxy.$Proxy63.persist(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:298)
at com.sun.proxy.$Proxy63.persist(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:506)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:503)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:488)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
**at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)**
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 65 more
rollbackFor = BusinessException.classbut are throwing aRuntimeExceptionin case of an error. The automatic rollback will take place only if the method throws aBusinessException, which it does not, so the rollback does not take place (as expected). - manishTransactionInterceptorto check this. The interceptor intercepts calls to the proxy generated for the Spring managed bean. With your Spring configuration (specifically<tx:jta-transaction-manager />), the proxy will be an instance ofSistemaBO(because the default proxies are JDK proxies which proxy interfaces not classes). So, unlessSistemaBOdeclares atestTransmethod with@Transactional, there may not be a transaction running when the test runs. - manish