Spring 服务@Transactional 不回滚事务Mybatis SqlSession
Spring Service @Transactional doesn't rollback transaction Mybatis SqlSession
目标是在失败的情况下回滚 all/any 事务。但这并没有像预期的那样工作。
我们使用SpringMVC + JMS + Service + Mybatis。在日志中,JMS 设置为回滚,但插入行而不是回滚。想知道我遗漏了什么或做错了什么?
@Transactional 标签是最近添加的。所以不确定它是否按预期工作。
代码:
服务Class:
@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 上下文
<bean id="dataExchLogic" class="com.abc.service.logic.DataExchLogic">
<property name="dataExchDao" ref="dataExchDao" />
</bean>
EAR/WAR 项目 Spring 上下文
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
<tx:annotation-driven transaction-manager="transactionManager" />
日志:
[31mWARN [0;39m [36mo.s.j.l.DefaultMessageListenerContainer[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.s.j.l.DefaultMessageListenerContainer[0;39m # Successfully refreshed JMS Connection
[39mDEBUG[0;39m [36mo.s.j.l.DefaultMessageListenerContainer[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.c.i.q.d.m.N.create!selectKey[0;39m # ==> Preparing: SELECT ID.NEXTVAL FROM DUAL
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create!selectKey[0;39m # ==> Parameters:
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create!selectKey[0;39m # <== Total: 1
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create[0;39m # ==> Preparing: INSERT INTO TABLE ( COL1, COL2, COL N) VALUES ( ?, CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?)
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create[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.c.i.q.d.m.N.create[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]
编辑 1:
控制器代码:
@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 更改:
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);
}
引自 spring 文档:
You can place the @Transactional annotation before an interface
definition, a method on an interface, a class definition, or a public
method on a class. However, the mere presence of the @Transactional
annotation is not enough to activate the transactional behavior. The
@Transactional annotation is simply metadata that can be consumed by
some runtime infrastructure that is @Transactional-aware and that can
use the metadata to configure the appropriate beans with transactional
behavior. In the preceding example, the
element switches on the transactional behavior.
也就是说,
void create(DataExch dataExch);
应该是
public void create(DataExch dataExch);
@Transactional
如果未应用于 public 方法,则不会显示注释行为。
编辑:
由于我的回答被否决了,为了支持我的回答并阐明当带事务注释的方法调用不带注释的方法时的事务行为,请看一下:
@Transactional method calling another method without @Transactional anotation? 特别是 Arun P Johny 的回答
请将 transactionManager 配置和 tx:annotation-driven 放入根 spring context
规则:Root context can see all the beans which Spring created. Child context(any Web Context) can see only its own beans.
在此特定情况下,tx:annotation-driven
在 Web 上下文中查找带有 @Transactional
注释的 bean。它找不到任何内容,因为您在根上下文中定义了 dataExchLogic
。这就是为什么你没有任何交易行为。
解决方案意味着将 tx:annotation-driven
移动到根上下文,因为 Root Context
可以找到在根或任何 Web 上下文中定义的任何 bean。
我想到了两种可能。
1) 你的 DAO class 正在开始一个新的交易。
2) 你的 DAO class 没有参与交易。
我看不出有任何其他原因应该将数据更新到数据库中。您能否将以下 属性 添加到 log4j 以查看正在启动的事务数。
log4j.logger.org.springframework.transaction.interceptor = trace
还要在服务和 DAO 方法中设置以下事务状态,以查看事务是否处于活动状态。
TransactionSynchronizationManager.isActualTransactionActive()
让我们知道发生了什么。
目标是在失败的情况下回滚 all/any 事务。但这并没有像预期的那样工作。
我们使用SpringMVC + JMS + Service + Mybatis。在日志中,JMS 设置为回滚,但插入行而不是回滚。想知道我遗漏了什么或做错了什么?
@Transactional 标签是最近添加的。所以不确定它是否按预期工作。
代码:
服务Class:
@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 上下文
<bean id="dataExchLogic" class="com.abc.service.logic.DataExchLogic">
<property name="dataExchDao" ref="dataExchDao" />
</bean>
EAR/WAR 项目 Spring 上下文
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.transaction.jta.WebSphereUowTransactionManager" />
<tx:annotation-driven transaction-manager="transactionManager" />
日志:
[31mWARN [0;39m [36mo.s.j.l.DefaultMessageListenerContainer[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.s.j.l.DefaultMessageListenerContainer[0;39m # Successfully refreshed JMS Connection
[39mDEBUG[0;39m [36mo.s.j.l.DefaultMessageListenerContainer[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.c.i.q.d.m.N.create!selectKey[0;39m # ==> Preparing: SELECT ID.NEXTVAL FROM DUAL
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create!selectKey[0;39m # ==> Parameters:
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create!selectKey[0;39m # <== Total: 1
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create[0;39m # ==> Preparing: INSERT INTO TABLE ( COL1, COL2, COL N) VALUES ( ?, CURRENT_TIMESTAMP, ?, ?, ?, ?, ?, ?)
[39mDEBUG[0;39m [36mg.c.i.q.d.m.N.create[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.c.i.q.d.m.N.create[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]
编辑 1:
控制器代码:
@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 更改:
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);
}
引自 spring 文档:
You can place the @Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class. However, the mere presence of the @Transactional annotation is not enough to activate the transactional behavior. The @Transactional annotation is simply metadata that can be consumed by some runtime infrastructure that is @Transactional-aware and that can use the metadata to configure the appropriate beans with transactional behavior. In the preceding example, the element switches on the transactional behavior.
也就是说,
void create(DataExch dataExch);
应该是
public void create(DataExch dataExch);
@Transactional
如果未应用于 public 方法,则不会显示注释行为。
编辑:
由于我的回答被否决了,为了支持我的回答并阐明当带事务注释的方法调用不带注释的方法时的事务行为,请看一下:
@Transactional method calling another method without @Transactional anotation? 特别是 Arun P Johny 的回答
请将 transactionManager 配置和 tx:annotation-driven 放入根 spring context
规则:Root context can see all the beans which Spring created. Child context(any Web Context) can see only its own beans.
在此特定情况下,tx:annotation-driven
在 Web 上下文中查找带有 @Transactional
注释的 bean。它找不到任何内容,因为您在根上下文中定义了 dataExchLogic
。这就是为什么你没有任何交易行为。
解决方案意味着将 tx:annotation-driven
移动到根上下文,因为 Root Context
可以找到在根或任何 Web 上下文中定义的任何 bean。
我想到了两种可能。 1) 你的 DAO class 正在开始一个新的交易。 2) 你的 DAO class 没有参与交易。
我看不出有任何其他原因应该将数据更新到数据库中。您能否将以下 属性 添加到 log4j 以查看正在启动的事务数。
log4j.logger.org.springframework.transaction.interceptor = trace
还要在服务和 DAO 方法中设置以下事务状态,以查看事务是否处于活动状态。
TransactionSynchronizationManager.isActualTransactionActive()
让我们知道发生了什么。