我们项目的框架都是使用的Spring,spring分为 编程式事务,在代码中硬编码。声明式事务,在配置文件中配置(推荐使用)
成都创新互联自2013年创立以来,是专业互联网技术服务公司,拥有项目网站制作、成都网站设计网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元槐荫做网站,已为上家服务,为槐荫各地企业和个人服务,联系电话:18982081108
声明式事务又分为两种:基于XML的声明式事务基于注解的声明式事务。我一般都是通过注解来进行的事务控制。也就是@Transactional
我们都是把注解加到需要使用事务控制的方法上,也可以加到类上,加到类上是给类里的所有的方法都加了事务,不建议这样做,这样会增加不需要使用事务的接口的响应时长。
@Transactional注解只能用在public 方法上,如果用在protected或者private的方法上,不会报错,但是该注解不会生效。
@Transactional注解只能回滚非检查型异常,具体为RuntimeException及其子类。
TransactionDefinition 接口中定义了五个表示隔离级别的常量:
TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,
Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别。
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取未提交的数据变更,可能会导致脏读、幻读或不可重复读。
TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;
如果当前没有事务,则创建一个新的事务。
TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;
如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)不支持当前事务的情况:
不支持当前事务的情况:
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,
如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,
如果当前存在事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,
如果当前存在事务,则抛出异常。
其他情况:
TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
Spring事务的实现都是依靠AOP,本质上也是依靠代理来实现。事务在spring中的实现其实就是生成bean对象的代理对象。
在bean进行创建出实例时, 如果是有事务注解的方法,就会被进行增强,最终形成代理类。在spring中,有两种动态代理的方式,一种是jdk,它是将原始对象放入代理对象内部,通过调用内含的原始对象来实现原始的业务逻辑,而另一种是cglib,它是通过生成原始对象的子类,子类复写父类的方法,从而实现对父类的增强。
jdk中,如果是private的方法,显然是无法访问的,而在cglib中,也是同样。所以总结来说private方法不能被继承,final方法不能被重写,static方法和继承不相干,所以它们3个的事务不起作用。
Spring选择让protected方法和package方法不支持事务,所以只有public方法支持事务
原因:
Spring事务管理用的是AOP,AOP底层用的是动态代理。所以spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction。
解决方式:
上面说了:动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!所以我们就使用代理对象来调用,就会触发事务;
综上解决方案,那怎么获取代理对象呢? 这里提供两种方式:
Spring事务管理用的是AOP,AOP底层用的是动态代理。所以spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。
此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。如果是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们说只有一个事务。
1.把方法B抽离到另外一个XXService中去,在这个Service中注入XXService,使用XXService调用方法B;
2.通过在方法内部获得当前类代理对象的方式,通过代理对象调用方法B
上面说了:动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!所以我们就使用代理对象来调用,就会触发事务;
综上解决方案,那怎么获取代理对象呢? 这里提供两种方式:
1.使用 ApplicationContext 上下文对象获取该对象;
2.使用 AopContext.currentProxy() 获取代理对象,但是需要配置exposeProxy=true
TestService testService = (TestService)AopContext.currentProxy();
testService.B();
同时还需要在B方法将传播行为配置为 @Transactional(propagation = Propagation.REQUIRES_NEW)
@Transactional是spring中声明式事务管理的注解配置方式。@Transactional注解可以帮助我们把事务开启、提交或者回滚的操作,通过aop的方式进行管理。
通过@Transactional注解就能让spring为我们管理事务,免去了重复的事务管理逻辑,减少对业务代码的侵入,使我们开发人员能够专注于业务层面开发。
我们知道实现@Transactional原理是基于spring aop,aop又是动态代理模式的实现,下面我们就详细分析一下实现原理
源码分析
网站栏目:Spring事务@Transactional注解面试及原理
标题链接:http://www.mswzjz.cn/qtweb/news32/17182.html
攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能