• 关于@Transactionsal注解


    一 . 关于事务

      1 .事务的特性:

        1) 原子性(Atomicity) 事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行。
        2) 一致性(Consistemcy) 事务前后,数据库的状态都满足所有的完整性约束。
        3) 隔离性(Isolation) 并发执行的事务是隔离的,一个不影响一个。通过设置数据库的隔离级别,可以达到不同的隔离效果
        4) 持久性(Durability) 在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

      2 .@Transactional注解的配置项:

        @Transactional(propagation=Propagation.REQUIRED) //控制事务传播。默认是Propagation.REQUIRED
        @Transactional(isolation=Isolation.DEFAULT) //控制事务隔离级别。默认跟数据库的隔离级别相同
        @Transactional(readOnly=false) //控制事务可读写、只可读。默认可读写
        @Transactional(timeout=30) //控制事务的超时时间,单位秒。默认跟数据库的事务控制系统相同
        @Transactional(rollbackFor=RuntimeException.class) //控制事务遇到哪些异常会回滚。默认是RuntimeException
        @Transactional(rollbackForClassName=RuntimeException) //同上
        @Transactional(noRollbackFor=NullPointerException.class) //控制事务遇到哪些异常不会回滚。默认遇到RuntimeException回滚
        @Transactional(noRollbackForClassName=NullPointerException)//同上

      3 .事务的7种传播特性:

        1) REQUIRED 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
        2) SUPPORTS 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
        3) MANDATORY 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
        4) NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行
        5) NEVER 总是非事务地执行,如果存在一个活动事务,则抛出异常
        6) REQUIRES_NEW 总是开启一个新的事务。如果一个事务已经存在,则将这个已经存在的事务挂起
        7) NOT_SUPPORTED 总是非事务地执行,并挂起任何存在的事务

    二 . 关于方法调用引起的事务

      1. 方法a()调用带有事务的方法b()

      方法a()没有事务 方法a()有事务
    REQUIRED  b()创建自己的事务 b()接受a()的事务
    SUPPORTS  b()不创建自己的事务  b()接受a()的事务
    MANDATORY  b()报异常 b()接受a()的事务
    NESTED  b()创建自己的事务 b()接受a()的事务,成为a()嵌套的子事务
    NEVER  b()不创建自己的事务 b()报异常
    REQUIRES_NEW  b()创建自己的事务 b()不接受a()的事务,b()先执行,内层事务失败不会影响外层事务
    NOT_SUPPORTED  b()不创建自己的事务 b()不接受a()的事务,b()先执行

    以上详细内容参考:https://www.cnblogs.com/lukelook/p/11246180.html

    三 . 使用@Transactional注解,在方法中抛出异常时,回滚不生效

      1. 可能的原因:
        1) 数据库引擎要支持事务
              如果是MySQL,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的

        2) 是否开启了对注解的解析

              配置文件必须加<tx:annotation-driven />,否则不解析@Transactional

      2. 解决办法:

        1) 如果是MySQL数据库,查看引擎是否是支持事务的引擎

        2) 抛出的异常是否是注解的回滚异常,默认是RuntimeException或者Error

          在业务代码中,有如下两种情况,比如:

          throw new RuntimeException("xxxxxxxxxxxx"); 事务回滚
          throw new Exception("xxxxxxxxxxxx"); 事务没有回滚

          此时需要将@Transactional注解的rollbackFor参数赋值为Exception.class(或者某个特定异常的class),此时将支持该异常回滚

        3) 如果使用try-catch捕获抛出的unchecked异常后没有在catch块中采用页面硬编码的方式使用spring api对事务做显式的回滚,则事务不会回滚,

         “将异常捕获,并且在catch块中不对事务做显式提交=生吞掉异常”,如果非要catch,在做完你想做的工作后一定要抛出可引起回滚的异常,否则spring会将你的操作commit

      3. 针对是否捕获异常那么现在有两个情况:

        情况1:如果没有在程序中手动捕获异常

    1 @Transactional(rollbackFor = { Exception.class })
    2 public void test() throws Exception {
    3      doDbStuff1();
    4      doDbStuff2();
    5     //假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作会回滚。
    6 }

        情况2:如果在程序中自己捕获了异常

    1 @Transactional(rollbackFor = { Exception.class })
    2 public void test() {
    3      try {
    4         doDbStuff1();
    5         doDbStuff2();//假如这个操作数据库的方法会抛出异常,现在方法doDbStuff1()对数据库的操作  不会回滚。
    6      } catch (Exception e) {
    7            e.printStackTrace();   
    8      }
    9 }

        现在如果我们需要手动捕获异常,并且也希望抛异常的时候能回滚肿么办呢?
        方法一:

     1 @Transactional(rollbackFor = { Exception.class })
     2 public void test() {
     3      try {
     4         doDbStuff1();
     5         doDbStuff2();
     6      } catch (Exception e) {
     7           e.printStackTrace();   
     8           TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();//就是这一句了,加上之后,如果doDbStuff2()抛了异常,                                                                                       //doDbStuff1()是会回滚的
     9      }
    10 }

    以上内容详细参考:https://blog.csdn.net/qq_24084925/article/details/53116846

        需要注意的是:在方法一中,使用的 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 会将方法事务设置为只回滚,无法commit

    如果想要实现正常的回滚和提交,可以使用 savepoint 方式:

     1 @Transactional(rollbackFor = { Exception.class })
     2 public void test() {
     3     Object savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
     4     try {
     5         doDbStuff1();
     6         doDbStuff2();
     7      } catch (Exception e) {
     8           e.printStackTrace();   
     9           TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);//如果抛了异常,会回滚到savepoint
    10      }
    11 }

        方法二:

          在捕获异常后,再抛出一个可以被事务识别的异常,使其回滚

     1 @Transactional(rollbackFor = { Exception.class })
     2 public void test() {
     3      try {
     4         doDbStuff1();
     5         doDbStuff2();
     6      } catch (Exception e) {
     7           e.printStackTrace();   
     8           throw new Exception();
     9      }
    10 }

          需要注意的是:此种方法在捕获后进行的操作,只有在回滚后不影响其效果时使用,如,打印日志操作,回滚后不会将已经打印好的日志删除

      4 . spring动态代理为事务埋下的坑

        描述:spring中的事务控制是通过动态代理实现的, 在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务.由于spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!通俗点讲就是一个类的AOP动态代理,只有在其方法被其他类中的方法调用时才会触发(使用触发是否恰当留待后续

    ),后续的通过本类的方法调用本类的方法便不会再次触发动态代理,也就无法再创建事务

        解决办法:

          办法一:可以把方法B放到另外一个service或者dao,然后把这个server或者dao通过@Autowired注入到方法A的bean里面,这样即使方法A没用事务,方法B也可以执行自己的事务了。(将方法b(抽离到其他bean中,这样在调用时会重新触发动态代理))

          办法二:通过AOP代理调用b方法即可走事务切面

    1 public class TestService{
    2     public void a() {  
    3         TestService aopProxy = (TestService)AopContext.currentProxy();
    4         aopProxy.b();//即调用AOP代理对象的b方法即可执行事务切面进行事务增强  
    5     }
    6 }

    详情参考:https://www.iteye.com/topic/35907/

    https://www.cnblogs.com/sung1024/archive/2004/01/13/11700449.html

    如何获取代理对象,可以参考:

    https://blog.csdn.net/dapinxiaohuo/article/details/52092447

    每天一点点

  • 相关阅读:
    vue禁止用户复制文案
    html2canvas.js + jspdf.js 实现html转pdf / html转图片
    Vue.js +pdf.js 处理响应pdf文件流数据,前端转图片预览不可下载
    JavaScript处理后端返回PDF文件流,在线预览下载PDF文件
    多线程并发工具类01-CountDownLatch 线程工具类
    线程池01-线程池基础知识
    网络基础知识01-协议分层与TCP/IP协议簇
    网络基础知识02-HTTP协议
    jquery-i18n 多语言切换
    springboot-01 springboot 启动 enviroment环境加载
  • 原文地址:https://www.cnblogs.com/xiao-lin-unit/p/14237838.html
Copyright © 2020-2023  润新知