Spring @Transactional service does not roll back Mybatis SqlSession transaction

The goal is to roll back all / any transactions in the event of a failure. But this does not work properly.

We use Spring MVC + JMS + Service + Mybatis. In JMS logs configured to rollback, but the line is inserted, not rollback. I would like to know what am I missing or is something wrong?

The @Transactional tag has recently been added. Therefore, Iโ€™m not sure that it works as expected.

the code:

Class of service:

@Transactional(value = "transactionManager", propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public class DataExchangeLogic implements DataExchangeService { private DataExchDao dataExchDao; ... @Override public void save(DataExch dataExch) throws ValidationException { if (dataExch.getId() != null && dataExch.getId() > 0) { this.dataExchDao.update(dataExch); } else { //LOGGER.debug("in insert::"); this.dataExchDao.create(dataExch); //Empty exception throw to test rollback throw new RuntimeException(); } } } 

DAO:

 public interface DataExchDaoMybatis extends NotificationDao { void create(DataExch dataExch); } 

Spring Context

 <bean id="dataExchLogic" class="com.abc.service.logic.DataExchLogic"> <property name="dataExchDao" ref="dataExchDao" /> </bean> 

EAR / WAR Spring Project

 <!-- Transaction Manager --> <bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager" /> <tx:annotation-driven transaction-manager="transactionManager" /> 

Logs:

 [31mWARN [0;39m [36mo.sjlDefaultMessageListenerContainer[0;39m # Setup of JMS message listener invoker failed for destination 'queue://REQUEST?priority=1&timeToLive=500000' - trying to recover. Cause: Transaction rolled back because it has been marked as rollback-only org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:720) at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:240) at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1142) at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1134) at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:1031) at java.lang.Thread.run(Thread.java:745) [34mINFO [0;39m [36mo.sjlDefaultMessageListenerContainer[0;39m # Successfully refreshed JMS Connection [39mDEBUG[0;39m [36mo.sjlDefaultMessageListenerContainer[0;39m # Received message of type [class com.ibm.ws.sib.api.jms.impl.JmsTextMessageImpl] from consumer [com.ibm.ws.sib.api.jms.impl.JmsQueueReceiverImpl@6ca01c74] of transactional session [com.ibm.ws.sib.api.jms.impl.JmsQueueSessionImpl@3ac3b63] Creating a new SqlSession Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@206ee277] JDBC Connection [com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@19b89f0c] will be managed by Spring [39mDEBUG[0;39m [36mg.ciqdmNcreate!selectKey[0;39m # ==> Preparing: SELECT ID.NEXTVAL FROM DUAL [39mDEBUG[0;39m [36mg.ciqdmNcreate!selectKey[0;39m # ==> Parameters: [39mDEBUG[0;39m [36mg.ciqdmNcreate!selectKey[0;39m # <== Total: 1 [39mDEBUG[0;39m [36mg.ciqdmNcreate[0;39m # ==> Preparing: INSERT INTO TABLE ( COL1, COL2, COL N) VALUES ( ?, CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?) [39mDEBUG[0;39m [36mg.ciqdmNcreate[0;39m # ==> Parameters: 468(Integer), SYSTEM(String), 2017-03-01 00:00:00.0(Timestamp), 2017-03-16 00:00:00.0(Timestamp), true(Boolean), test 112(String), ALL(String) [39mDEBUG[0;39m [36mg.ciqdmNcreate[0;39m # <== Updates: 1 Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@206ee277] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@206ee277] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@206ee277] 

EDIT 1:

Controller Code:

 @ResourceMapping(value = "addNewDEURL") public void addNewDE(@ModelAttribute(value = "dataObject") final DataExch dataExch, final BindingResult bindingResult, final ResourceResponse response) { if (!bindingResult.hasErrors()) { try { dataExchangeService.save(dataExch); } catch (final ValidationException e) { logger.error("A validation exception occurred.", e); } } else { logger.error(bindingResult.getAllErrors().get(0) .getDefaultMessage()); } } 

DAO changed:

 public class DataExchDaoMybatis extends BaseDaoImpl implements DataExchDao { public void create(DataExch dataExch) { doSimpleInsert("insertDE", dataExch); } } 

BaseDaoImpl:

 public void doSimpleInsert(String queryId, Object o) { SqlSession sqlSession = sqlSessionFactory.openSession(); sqlSession.insert(queryId, o); } 
+8
spring spring-jms transactions spring-mybatis mybatis
source share
3 answers

Put transactionManager configuration and tx: annotation in spring root context

enter image description here

Rule: Root context can see all the beans which Spring created. Child context(any Web Context) can see only its own beans. Root context can see all the beans which Spring created. Child context(any Web Context) can see only its own beans.

In this particular case, tx:annotation-driven looks for beans with the @Transactional annotation in the Web context. It cannot find anything because you defined dataExchLogic in the root context. That is why you did not have transactional behavior.

@EnableTransactionManagement and searches only @Transactional on beans in the same application context in which they are defined. This means that if you put configuration annotations in the WebApplicationContext for the DispatcherServlet, it only checks @Transactional beans on your controllers, and not on your services. See Section 21.2, โ€œDispatcherServletโ€ for more information.

The solution involves moving tx:annotation-driven to the root context, because the Root Context can find any bean defined both in the root and in any web context.

+4
source share

I think of two possibilities. 1) Your DAO class starts a new transaction. 2) Your DAO class is not involved in the transaction.

I see no other reason why the data should be updated in the database. You can add the log4j property below to see how many transactions have started.

 log4j.logger.org.springframework.transaction.interceptor = trace 

Also syosut the transaction status below in Service and DAO to find out if a transaction is active.

 TransactionSynchronizationManager.isActualTransactionActive() 

Let us know what will happen.

+2
source share

Quote from spring documentation:

You can put the @Transactional annotation in front of an interface definition, a method on the interface, a class definition, or a public method by class. However, the mere presence of @Transactional annotations is not enough to activate transactional behavior. @Transactional annotations are simply metadata that some runtime infrastructure can use, which is @ Transactional-aware, and which can use metadata to configure the appropriate beans with transactional behavior. In the previous example, the element toggles the transactional behavior.

It means that

void create(DataExch dataExch);

it should be

public void create(DataExch dataExch);

@Transactional annotation behavior is not displayed unless it applies to a public method.

EDIT:

Since my answer was omitted to support my answer and shed light on transactional behavior when the annotated Transactional method calls the method without annotation, look at this:

@Transactional method calling another method without @Transactional anotation? in particular the answer of Aruna P. Johnny

+1
source share

All Articles