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

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

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

  1. /**
  2. * 删除多条记录
  3. */
  4. @Override
  5. public ShopResult deleteContentGroup(String[] ids) {
  6. if (null == ids || ids.length == 0)
  7. {
  8. return ShopResult.error();
  9. }
  10. for (String idStr : ids)
  11. {
  12. Long id = new Long(idStr);
  13. contentMappger.deleteByPrimaryKey(id);
  14. }
  15. contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
  16. return ShopResult.ok();
  17. }

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

  1. ### SQL: delete form tb_content where kid = ?
  2. ### 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
  3. where kid = 31' at line 1
  4. ; 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
  5. where kid = 31' at line 1] with root cause
  6. 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
  7. where kid = 31' at line 1
  8. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  9. at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
  10. at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  11. at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
  12. at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
  13. at com.mysql.jdbc.Util.getInstance(Util.java:381)
  14. at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1030)
  15. at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
  16. at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3536)
  17. at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3468)
  18. at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1957)
  19. ..................
  20. at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
  21. at com.sun.proxy.$Proxy35.updateWithErrTest(Unknown Source)
  22. at com.shop.manager.service.impl.ContentServiceImpl.deleteContentGroup(ContentServiceImpl.java:94)
  23. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  24. at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  25. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  26. at java.lang.reflect.Method.invoke(Method.java:498)
  27. at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
  28. at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
  29. .......

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

代码

  1. /**
  2. * 删除多条记录
  3. */
  4. @Override
  5. public ShopResult deleteContentGroup(String[] ids) {
  6. if (null == ids || ids.length == 0)
  7. {
  8. return ShopResult.error();
  9. }
  10. for (String idStr : ids)
  11. {
  12. Long id = new Long(idStr);
  13. contentMappger.deleteByPrimaryKey(id);
  14. }
  15. try {
  16. contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
  17. } catch (Exception e) {
  18. //捕获异常,但不处理
  19. System.out.println("-----nothing to do-------");
  20. }
  21. return ShopResult.ok();
  22. }

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

  1. ### The error occurred while setting parameters
  2. ### SQL: delete form tb_content where kid = ?
  3. ### 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
  4. where kid = 31' at line 1
  5. ]
  6. -----nothing to do-------
  7. 2017-06-18 14:27:59,493 [http-bio-8080-exec-4] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession //(事务提交)
  8. [org.apache.ibatis.session.defaults.DefaultSqlSession@616a85a9]

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

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

  1. /**
  2. * 删除多条记录
  3. */
  4. @Override
  5. public ShopResult deleteContentGroup(String[] ids) {
  6. if (null == ids || ids.length == 0)
  7. {
  8. return ShopResult.error();
  9. }
  10. for (String idStr : ids)
  11. {
  12. Long id = new Long(idStr);
  13. contentMappger.deleteByPrimaryKey(id);
  14. }
  15. try {
  16. contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
  17. } catch (Exception e) {
  18. System.out.println("----throw Exception-----");
  19. throw new RuntimeException();
  20. }
  21. return ShopResult.ok();
  22. }

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

  1. ### SQL: delete form tb_content where kid = ?
  2. ### 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
  3. where kid = 31' at line 1
  4. ]
  5. ----throw Exception-----
  6. 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]
  7. ...............
  8. 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
  9. 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
  10. 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
  11. 2017-06-18 14:21:27,942 [http-bio-8080-exec-1] [org.springframework.web.servlet.DispatcherServlet]-[DEBUG] Could not complete request
  12. java.lang.RuntimeException
  13. at com.shop.manager.service.impl.ContentServiceImpl.deleteContentGroup(ContentServiceImpl.java:98) //异常
  14. at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

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

代码:

  1. /**
  2. * 删除多条记录
  3. */
  4. @Override
  5. public ShopResult deleteContentGroup(String[] ids) {
  6. if (null == ids || ids.length == 0)
  7. {
  8. return ShopResult.error();
  9. }
  10. for (String idStr : ids)
  11. {
  12. Long id = new Long(idStr);
  13. contentMappger.deleteByPrimaryKey(id);
  14. }
  15. try {
  16. contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
  17. } catch (Exception e) {
  18. //捕获异常,继续抛出
  19. System.out.println("-----throw Exception-------");
  20. throw e;
  21. }
  22. return ShopResult.ok();
  23. }

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

  1. ### The error occurred while setting parameters
  2. ### SQL: delete form tb_content where kid = ?
  3. ### 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
  4. where kid = 31' at line 1
  5. ]
  6. -----throw Exception-------
  7. 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]
  8. 2017-06-18 14:36:25,308 [http-bio-8080-exec-9] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization closing SqlSession //事务回滚
  9. [org.apache.ibatis.session.defaults.DefaultSqlSession@45fe0f70]

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

代码:

  1. /**
  2. * 删除多条记录
  3. * @throws Exception
  4. */
  5. @Override
  6. public ShopResult deleteContentGroup(String[] ids) throws Exception {
  7. if (null == ids || ids.length == 0)
  8. {
  9. return ShopResult.error();
  10. }
  11. for (String idStr : ids)
  12. {
  13. Long id = new Long(idStr);
  14. contentMappger.deleteByPrimaryKey(id);
  15. }
  16. try {
  17. contentMappger.updateWithErrTest(31L); //错误代码,SQL语句错误。用来测试事务,看是否回滚
  18. } catch (Exception e) {
  19. //捕获异常,抛出新异常
  20. System.out.println("-----throw new Exception(e)-------");
  21. throw new Exception(e);
  22. }
  23. return ShopResult.ok();
  24. }

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

  1. ### The error occurred while setting parameters
  2. ### SQL: delete form tb_content where kid = ?
  3. ### 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
  4. where kid = 31' at line 1
  5. ]
  6. -----throw new Exception(e) -------
  7. 2017-06-18 14:43:16,098 [http-bio-8080-exec-10] [org.mybatis.spring.SqlSessionUtils]-[DEBUG] Transaction synchronization committing SqlSession //事务提交
  8. [org.apache.ibatis.session.defaults.DefaultSqlSession@32c4821]
  9. 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

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

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

  1. ### The error may involve com.shop.manager.mapper.TbContentMapper.updateWithErrTest-Inline
  2. ### The error occurred while setting parameters
  3. ### SQL: delete form tb_content where kid = ?
  4. ### 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
  5. where kid = 31' at line 1
  6. ]
  7. -----throw new Exception-------
  8. 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]
  9. 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. mongodb windows的安装方法和添加到任务管理器中、检测是否成功、增删改查命令

    转: mongodb安装方法: https://blog.csdn.net/heshushun/article/details/77776706        mongodb检测安装成功 .以及增删改 ...

  2. [UE4]显示队友

  3. [UE4]Size To content自动适配大小

  4. int main(int argc,char* argv[])浅析

    int main(int argc,char* argv[])浅析 argc : 指输入参数个数,默认值1,就是执行程序名称 argv[] : 输入参数数组指针 举个栗子: 1. 编写一个argc.c ...

  5. U3D学习11——nav导航和动画

    一.nav导航 1.nav mesh agent 2.off mesh link 3.navigation面板-areas标签的应用,导航分层 二.动画 1.avatar可重用. 2.Animator ...

  6. (转)C#实现注册码

    原文地址:http://www.cnblogs.com/netcorner/archive/2011/08/31/2911922.html 开发软件时,当用到商业用途时,注册码与激活码就显得很重要了. ...

  7. es6 import export 引入导出变量方式

    var testdata='sdfkshdf'; //export testdata;//err export {testdata as ms}; export var firstName = 'Mi ...

  8. Android MVP案例;

    就一个十分简单的获取列表数据并展示的Demo:分别使用MVC和MVP实现: 先来一个假的数据源: //假设这就是数据源 public class UserBean { public static Li ...

  9. python requests 发起http POST 请求

    python requests 发起http POST 请求,带参数,带请求头: #!/usr/bin/env python # -*- coding: utf-8 -*- import reques ...

  10. day2作业(基本数据类型,语法)

    #coding:utf-8 '''1.写代码实现用户输入用户名和密码,当用户名为 seven 且 密码为 123 时,显示登陆成功,否则登陆失败!实现用户输入用户名和密码,当用户名为 seven 且 ...