探讨Spring事务控制中,异常触发事务回滚原理。文章进行了6种情况下的Spring事务是否回滚。 
以下代码都是基于Spring与Mybatis整合,使用Spring声明式事务配置事务方法。

1.不捕获异常(一般处理方式)

代码,其中contentMappger.updateWithErrTest(31L); 是SQL语句错误,用来测试回滚。

    /**
* 删除多条记录
*/
@Override
public ShopResult deleteContentGroup(String[] ids) {
if (null == ids || ids.length == 0)
{
return ShopResult.error();
}
for (String idStr : ids)
{
Long id = new Long(idStr);
contentMappger.deleteByPrimaryKey(id);
}
contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
return ShopResult.ok();
}

运行结果:报错,事务发生了回滚,即由于错误代码,前面的for循环删除记录事务被回滚了。

### SQL: delete form tb_content    where kid = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
; bad SQL grammar []; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1] with root cause
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.Util.getInstance(Util.java:381)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1030)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3536)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3468)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1957)
..................
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
at com.sun.proxy.$Proxy35.updateWithErrTest(Unknown Source)
at com.shop.manager.service.impl.ContentServiceImpl.deleteContentGroup(ContentServiceImpl.java:94)
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:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
.......

2. 捕获异常,但不处理,不抛出

代码

    /**
* 删除多条记录
*/
@Override
public ShopResult deleteContentGroup(String[] ids) {
if (null == ids || ids.length == 0)
{
return ShopResult.error();
}
for (String idStr : ids)
{
Long id = new Long(idStr);
contentMappger.deleteByPrimaryKey(id);
}
try {
contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
} catch (Exception e) {
//捕获异常,但不处理
System.out.println("-----nothing to do-------");
}
return ShopResult.ok();
}

运行结果:事务提交,未回滚。 

### The error occurred while setting parameters
### SQL: delete form tb_content where kid = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
]
-----nothing to do-------
2017-06-18 14:27:59,493 [http-bio-8080-exec-4] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession //(事务提交)
[org.apache.ibatis.session.defaults.DefaultSqlSession@616a85a9]

3. 捕获异常,并抛出RuntimeException异常

Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error。 
代码

    /**
* 删除多条记录
*/
@Override
public ShopResult deleteContentGroup(String[] ids) {
if (null == ids || ids.length == 0)
{
return ShopResult.error();
}
for (String idStr : ids)
{
Long id = new Long(idStr);
contentMappger.deleteByPrimaryKey(id);
}
try {
contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
} catch (Exception e) {
System.out.println("----throw Exception-----");
throw new RuntimeException();
}
return ShopResult.ok();
}

运行结果:如预期的一样,抛出RuntimeException,事务发生回滚。

### SQL: delete form tb_content    where kid = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
]
----throw Exception-----
2017-06-18 14:21:27,928 [http-bio-8080-exec-1] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3ef56e3a]
...............
2017-06-18 14:21:27,941 [http-bio-8080-exec-1] [org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver]-[DEBUG] Resolving exception from handler [public com.shop.common.pojo.ShopResult com.shop.manager.controller.ContentController.deleteContentGroup(java.lang.String)]: java.lang.RuntimeException
2017-06-18 14:21:27,941 [http-bio-8080-exec-1] [org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver]-[DEBUG] Resolving exception from handler [public com.shop.common.pojo.ShopResult com.shop.manager.controller.ContentController.deleteContentGroup(java.lang.String)]: java.lang.RuntimeException
2017-06-18 14:21:27,942 [http-bio-8080-exec-1] [org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver]-[DEBUG] Resolving exception from handler [public com.shop.common.pojo.ShopResult com.shop.manager.controller.ContentController.deleteContentGroup(java.lang.String)]: java.lang.RuntimeException
2017-06-18 14:21:27,942 [http-bio-8080-exec-1] [org.springframework.web.servlet.DispatcherServlet]-[DEBUG] Could not complete request
java.lang.RuntimeException
at com.shop.manager.service.impl.ContentServiceImpl.deleteContentGroup(ContentServiceImpl.java:98) //异常
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

4.捕获异常,并继续抛出原捕获的异常

代码:

    /**
* 删除多条记录
*/
@Override
public ShopResult deleteContentGroup(String[] ids) {
if (null == ids || ids.length == 0)
{
return ShopResult.error();
}
for (String idStr : ids)
{
Long id = new Long(idStr);
contentMappger.deleteByPrimaryKey(id);
}
try {
contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
} catch (Exception e) {
//捕获异常,继续抛出
System.out.println("-----throw Exception-------");
throw e;
}
return ShopResult.ok();
}

运行结果:抛出异常,事务发生回滚

### The error occurred while setting parameters
### SQL: delete form tb_content where kid = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
]
-----throw Exception-------
2017-06-18 14:36:25,308 [http-bio-8080-exec-9] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@45fe0f70]
2017-06-18 14:36:25,308 [http-bio-8080-exec-9] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession //事务回滚
[org.apache.ibatis.session.defaults.DefaultSqlSession@45fe0f70]

5. 捕获异常,并抛出新new的异常(或自定义Exception异常) new Exception

代码:

    /**
* 删除多条记录
* @throws Exception
*/
@Override
public ShopResult deleteContentGroup(String[] ids) throws Exception {
if (null == ids || ids.length == 0)
{
return ShopResult.error();
}
for (String idStr : ids)
{
Long id = new Long(idStr);
contentMappger.deleteByPrimaryKey(id);
}
try {
contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
} catch (Exception e) {
//捕获异常,抛出新异常
System.out.println("-----throw new Exception(e)-------");
throw new Exception(e);
}
return ShopResult.ok();
}

运行结果:事务提交,未回滚。(Spring的默认回滚异常类型不包括Exception)

### The error occurred while setting parameters
### SQL: delete form tb_content where kid = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
]
-----throw new Exception(e) -------
2017-06-18 14:43:16,098 [http-bio-8080-exec-10] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession //事务提交
[org.apache.ibatis.session.defaults.DefaultSqlSession@32c4821]
2017-06-18 14:43:16,098 [http-bio-8080-exec-10] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@32c4821]

6. 在事务配置中没有设置rollback-for异常类型为Exception

<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean> <!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 事务行为控制 -->
<tx:attributes>
<tx:method name="save" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="create*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.shop.manager.service.*.*(..))" />
</aop:config>
    /**
* 删除多条记录
* @throws Exception
*/
@Override
public ShopResult deleteContentGroup(String[] ids) throws Exception {
if (null == ids || ids.length == 0)
{
return ShopResult.error();
}
for (String idStr : ids)
{
Long id = new Long(idStr);
contentMappger.deleteByPrimaryKey(id);
}
try {
contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
} catch (Exception e) {
//捕获异常,继续抛出
System.out.println("-----throw new Exception-------");
throw new Exception("---自定义Exception,事务中已配置rollback-for---");
}
return ShopResult.ok();
}

运行结果:如预期一样发生回滚

### The error may involve com.shop.manager.mapper.TbContentMapper.updateWithErrTest-Inline
### The error occurred while setting parameters
### SQL: delete form tb_content where kid = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tb_content
where kid = 31' at line 1
]
-----throw new Exception-------
2017-06-18 15:07:02,273 [http-bio-8080-exec-8] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f177061]
2017-06-18 15:07:02,273 [http-bio-8080-exec-8] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f177061]

总结:

  1. Spring事务管理是根据异常来进行回滚操作;
  2. Spring与Mybatis整合时,虽然在Service方法中并没有check异常,但是如果数据库有异常发生,默认会进行事务回滚。
  3. Spring 如果不添加rollbackFor等属性,Spring碰到Unchecked Exceptions都会回滚,不仅是RuntimeException,也包括Error。
  4. 如果在事务方法中捕获异常并进行处理,一定要继续抛出异常并在Spring事务管理中进行rollbak-for配置。

转自:https://blog.csdn.net/ljyhust/article/details/73431968?locationNum=5&fps=1

Spring事务管理——回滚(rollback-for)控制的更多相关文章

  1. Spring事务管理回滚问题

    Spring事务管理不能回滚问题 在前段时间学习SpringMVC的练习中,碰到声明式事务管理时,事务不能回滚的情况,通过查看博客和资料,解决了问题. 原因 导致Spring事务管理不能回滚的原因有两 ...

  2. Spring事务不回滚原因分析

    Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离. 在我完成一个项目的时候,遇到了一个Spring事务不回滚的问题,通过aspectJ和@Transacti ...

  3. springmvc mybatis 声明式事务管理回滚失效,(checked回滚)捕捉异常,传输错误信息

    一.知识点及问题 后端框架: Spring .Spring mvc .mybatis 业务需求: client先从服务端获取用户大量信息到client,编辑完毕之后统一Post至服务端,对于数据的改动 ...

  4. Spring事务异常回滚,捕获异常不抛出就不会回滚(转载) 解决了我一年前的问题

    最近遇到了事务不回滚的情况,我还考虑说JPA的事务有bug? 我想多了.......    为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志.但是这边情况来了,当这个方法异常 ...

  5. Spring事务异常回滚,捕获异常不抛出就不会回滚

    最近遇到了事务不回滚的情况,我还考虑说JPA的事务有bug? 我想多了.......    为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志.但是这边情况来了,当这个方法异常 ...

  6. 【转】Spring事务异常回滚,捕获异常不抛出就不会回滚

    最近遇到了事务不回滚的情况,我还考虑说JPA的事务有bug? 我想多了.......     为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志.但是这边情况来了,当这个方法异 ...

  7. Spring事务异常回滚

    最近遇到了事务不回滚的情况,我还考虑说JPA的事务有bug? 我想多了.......    为了打印清楚日志,很多方法我都加tyr catch,在catch中打印日志.但是这边情况来了,当这个方法异常 ...

  8. Spring3声明式事务处理事务无法回滚rollback分析(annotation与xml配置混用)

    新项目试运行,DBA提示生产数据库一个表的事务20分钟都未提交,分析过程如下: 1.查看日志log文件,最近20分钟是否有error日志: 2.发现某表有insert错误日志,初步判断由该表插入异常, ...

  9. spring事务没回滚

    最近遇见一个问题,用spring管理实务,在service层处理数据,保存数据时出现异常,但没有回滚,检查了一下,发现是因为我用try catch将异常进行捕获了,没有抛出导致的:默认spring事务 ...

随机推荐

  1. ArcMap VBA实现连续编号

    连续编号VBA部分:Static lCount as longlCount=lCount+1赋值部分:lCount (从1开始)lCount-1 (从0开始)

  2. 用最通俗的话解释AJAX是什么东西

    直白地说,就是没用AJAX的网页,你点一个按钮就要刷新一下页面,尽管新页面上只有一行字和当前页面不一样,但你还是要无聊地等待页面刷新. 用了AJAX之后,你点击,然后页面上的一行字就变化了,页面本身不 ...

  3. http请求状态及其含义表

    1xx - 信息提示 100 - 初始的请求已经接受,客户应当继续发送请求的其余部分.(HTTP 1.1新) 101 - 服务器将遵从客户的请求转换到另外一种协议.(HTTP 1.1新)   2xx ...

  4. Fragment onActivityResult提前响应,startActivityForResult执行后立即响应onActivityResult的解决方法

    找不到病根真是让人愁白了头: 今天写了一个startActivityForResult,开启一个Activity并拿到返回的结果,但是startActivityForResult刚走,onActivi ...

  5. RecyclerView.Adapter封装,最简单实用的BaseRecyclerViewAdapter;只需重写一个方法,设置数据链式调用;

    之前对ListView的BaseAdapter进行过封装,只需重写一个getView方法: 现在慢慢的RecyclerView成为主流,下面是RecyclerView.Adapter的封装: Base ...

  6. 自媒体运营排版利器----Markdown here

    Markdown Here ​ 下载chrome插件直接下载 使用:打开网页文章编辑器,比如cnblog 用markdown语法写文章,之后点击编译 可以设置好css语法,以后每次可以套用同样的模板 ...

  7. Python笔记:字典的fromkeys方法创建的初始value同内存地址问题

    dict中的fromkeys()函数可以通过一个list来创建一个用同一初始value的dict. d = dict.fromkeys(["苹果", "菠萝"] ...

  8. CentOS7离线安装Ambari与HDP

    安装步骤总体说明 1.安装以前需要先规划服务器,一台主机多台从机.主从需要设置时间同步和免密. 2.建立离线源,因在线安装速度很慢,所以需要建立离线源. 3.在主机上安装Ambari,进入系统后,挂接 ...

  9. [转]ORA-12560: TNS: 协议适配器错误

    转自:http://worms.blog.51cto.com/969144/1293265 Sqlplus 登陆oracle时报错ORA-12560:TNS: 协议适配器错误 如下:C:\Users\ ...

  10. The difference between Spring Tool Suite and Spring IDE

    The difference between Spring Tool Suite and Spring IDE Spring Tool Suite和Spring IDE的区别: http://down ...