转载自:http://tech.lede.com/2017/02/06/rd/server/SpringTransactional/

本文主要讨论Spring声明式事务中使用注解@Transactional的方式、原理及注意事项,主要包括以下内容:

  • Spring @Transactional的配置使用;
  • Spring @Transactional的传播行为和隔离级别;
  • Spring @Transactional的工作原理;
  • Spring @Transactional的注意事项;
  • Spring @Transactional自我调用中的问题。

1、Spring @Transactional的配置

步骤一、在Spring配置文件中引入命名空间

1

2

3

4

5

6

7

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

步骤二、xml配置文件中,添加事务管理器bean配置

<!-- 事务管理器配置,单数据源事务 --><bean id="pkgouTransactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="pkGouDataSource" /></bean>
<!-- 使用annotation定义事务 --><tx:annotation-driven transaction-manager="pkgouTransactionManager" />

步骤三、在使用事务的方法或者类上添加 @Transactional(“pkgouTransactionManager”)注解

2、 Spring @Transactional的传播行为和隔离级别

1> 事务注解方式: @Transactional

  • 标注在类前:标示类中所有方法都进行事务处理
  • 标注在接口、实现类的方法前:标示方法进行事务处理

2> 事务传播行为,共有六种介绍:

事务传播行为 说明
@Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况)
@Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务

3> 事务超时设置:
@Transactional(timeout=30) //默认是30秒

4> 事务隔离级别:

事务隔离级别 说明
@Transactional(isolation = Isolation.READ_UNCOMMITTED) 读取未提交数据(会出现脏读, 不可重复读),基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)(SQLSERVER默认) 读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ) 可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE) 串行化
  • 脏读 : 一个事务读取到另一事务未提交的更新数据
  • 不可重复读 : 在同一事务中, 多次读取同一数据返回的结果有所不同, 换句话说, 后续读取可以读到另一事务已提交的更新数据。相反,”可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据
  • 幻读 : 一个事务读到另一个事务已提交的insert数据

@Transactional的属性:

3、 Spring @Transactional的工作原理

  • 自动提交

默认情况下,数据库处于自动提交模式。每一条语句处于一个单独的事务中,在这条语句执行完毕时,如果执行成功则隐式的提交事务,如果执行失败则隐式的回滚事务。
     事务管理,是一组相关的操作处于一个事务之中,因此必须关闭数据库的自动提交模式。这点,Spring会在org/springframework/jdbc/datasource/DataSourceTransactionManager.java中将底层连接的自动提交特性设置为false。

1

2

3

4

5

6

7

8

9

10

11

12

// switch to manual commit if necessary。 this is very expensive in some jdbc drivers,

// so we don't want to do it unnecessarily (for example if we've explicitly

// configured the connection pool to set it already)。if (con。getautocommit())

{

txobject.setmustrestoreautocommit(true);

if (logger.isdebugenabled())

{

logger.debug("switching jdbc connection [" + con + "] to manual commit");

}

//首先将自动提交属性改为false

con.setautocommit(false);

}

  • spring事务回滚规则

Spring事务管理器回滚一个事务的推荐方法是在当前事务的上下文内抛出异常。Spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
       默认配置下,Spring只有在抛出的异常为运行时unchecked异常时才回滚该事务,也就是抛出的异常为RuntimeException的子类(Errors也会导致事务回滚)。而抛出checked异常则不会导致事务回滚。
       Spring也支持明确的配置在抛出哪些异常时回滚事务,包括checked异常。也可以明确定义哪些异常抛出时不回滚事务。
还可以编程性的通过setRollbackOnly()方法来指示一个事务必须回滚,在调用完setRollbackOnly()后你所能执行的唯一操作就是回滚。

4、 Spring @Transactional的注意事项

  • 由于Spring事务管理是基于接口代理或动态字节码技术,通过AOP实施事务增强的。

(1)对于基于接口动态代理的AOP事务增强来说,由于接口的方法是public的,这就要求实现类的实现方法必须是public的(不能是protected,private等),同时不能使用static的修饰符。所以,可以实施接口动态代理的方法只能是使用“public”或“public final”修饰符的方法,其它方法不可能被动态代理,相应的也就不能实施AOP增强,也即不能进行Spring事务增强。

(2)基于CGLib字节码动态代理的方案是通过扩展被增强类,动态创建子类的方式进行AOP增强植入的。由于使用final,static,private修饰符的方法都不能被子类覆盖,相应的,这些方法将不能被实施的AOP增强。

所以,必须特别注意这些修饰符的使用,@Transactional 注解只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。

  • 用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚。默认遇到运行期异常(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的异常时回滚;而遇到需要捕获的异常(throw new Exception(“注释”);)不会回滚,即遇到受检查的异常(就是非运行时抛出的异常,编译器会检查到的异常叫受检查异常或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception。class,其它异常}) 。如果让unchecked异常不回滚: @Transactional(notRollbackFor=RunTimeException.class)如下:

1

2

3

4

5

@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚

public void methodName()

{

throw new Exception("注释");

}

1

2

3

4

5

@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期异常(throw new RuntimeException("注释");)会回滚

public ItimDaoImpl getItemDaoImpl()

{

throw new RuntimeException("注释");

}

-仅仅 @Transactional注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被可以识别 @Transactional注解和上述的配置适当的具有事务行为的beans所使用。其实,根本上是 元素的出现 开启了事务行为。

  • Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类火方法上使用 @Transactional 注解。
  • @Transactional 注解标识的方法,处理过程尽量的简单。尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。
  • @Transactional 注解的默认事务管理器bean是“transactionManager”,如果声明为其他名称的事务管理器,需要在方法上添加@Transational(“managerName”)。
  • @Transactional 注解标注的方法中不要出现网络调用、比较耗时的处理程序,因为,事务中数据库连接是不会释放的,如果每个事务的处理时间都非常长,那么宝贵的数据库连接资源将很快被耗尽。

5、 Spring @Transactional自我调用中的问题

Spring事务使用AOP代理后的方法调用执行流程,如图所示:

从图中可以看出,调用事务时首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强。即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。

这样在自我调用时,则会出现无法开启事务的问题,比如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public interface TargetService

{

public void a();

public void b();

}

@Service

public class TargetServiceImpl implements TargetService

{

public void a()

{

this.b();

}

@Transactional(propagation = Propagation.REQUIRES_NEW)

public void b()

{

//执行数据库操作

}

}

此处的this指向目标对象,因此调用this.b()将不会执行b事务切面,即不会执行事务增强,因此b方法的事务定义“@Transactional(propagation = Propagation.REQUIRES_NEW)”将不会实施,即结果是b和a方法的事务是方法的事务定义是一样的。

解决方法

通过BeanPostProcessor 在目标对象中注入代理对象:

一、定义BeanPostProcessor 需要使用的标识接口

1

2

3

4

public interface BeanSelfAware

{

public abstract void setSelf(Object obj);

}

二、定义自己的BeanPostProcessor(InjectBeanSelfProcessor)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

public class InjectBeanSelfProcessor

implements BeanPostProcessor, ApplicationContextAware

{

ApplicationContext context;

private static Log log = LogFactory.getLog(com/netease/lottery/base/common/BeanSelf/InjectBeanSelfProcessor);

public InjectBeanSelfProcessor()

{

}

public void setApplicationContext(ApplicationContext context)

throws BeansException

{

this.context = context;

}

public Object postProcessAfterInitialization(Object bean, String beanName)

throws BeansException

{

if(bean instanceof BeanSelfAware)

{//如果Bean实现了BeanSelfAware标识接口,就将代理对象注入

BeanSelfAware myBean = (BeanSelfAware)bean;

Class cls = bean.getClass();

if(!AopUtils.isAopProxy(bean))

{

Class c = bean.getClass();

Service serviceAnnotation = (Service)c.getAnnotation(org/springframework/stereotype/Service);

if(serviceAnnotation != null)

try

{

bean = context.getBean(beanName);

if(AopUtils.isAopProxy(bean));

}

catch(BeanCurrentlyInCreationException beancurrentlyincreationexception) { }

catch(Exception ex)

{

log.fatal((new StringBuilder()).append("No Proxy Bean for service ").append(bean.getClass()).append(" ").append(ex.getMessage()).toString(), ex);

}

}

myBean.setSelf(bean);

return myBean;

} else

{

return bean;

}

}

public Object postProcessBeforeInitialization(Object bean, String beanName)

throws BeansException

{

return bean;

}

三、目标类实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public interface TargetService

{

public void a();

public void b();

}

@Service

public class TargetServiceImpl implements TargetService,BeanSelfAware

{

private TargetService self;

public void setSelf(Object proxyBean)

{ //通过InjectBeanSelfProcessor注入自己(目标对象)的AOP代理对象

this.self = (TargetService) proxyBean;

}

public void a()

{

self.b();

}

@Transactional(propagation = Propagation.REQUIRES_NEW)

public void b()

{

//执行数据库操作

}

}

postProcessAfterInitialization根据目标对象是否实现BeanSelfAware标识接口,通过setSelf(bean)将代理对象(bean)注入到目标对象中,从而可以完成目标对象内部的自我调用。

Spring 事务管理@transactional 的实现原理和使用相关推荐

  1. Spring事务管理 | 数据库连接池流程原理分析

  2. springaop事务逻辑原理_架构师:一篇文章掌握——Spring 事务管理

    对大多数Java开发者来说,Spring事务管理是Spring应用中最常用的功能,使用也比较简单.本文主要逐步介绍Spring事务管理的相关知识点及原理,作为Spring事务管理的学习总结. 一.关键 ...

  3. Spring事务管理(二)-TransactionProxyFactoryBean原理

    2019独角兽企业重金招聘Python工程师标准>>> 通常Spring事务管理的配置都是XML或者声明式注解的方式,然后想要学习其运行的原理,从TransactionProxyFa ...

  4. Spring事务管理的实现原理

    Spring事务管理的实现原理 文章目录 Spring事务管理的实现原理 背景 正文 第一轮学习:总体分析,目标确认 总体分析 第二轮学习:正式进入源码,寻找突破口 第二次分析 总结 第三轮学习:研究 ...

  5. 分析Spring事务管理原理及应用

    目录 一.Spring事务管理介绍 (一)基本理论 (二)实际工作中的举例 (三)简单应用举例 二.Spring事务配置介绍 (一)Spring事务属性介绍 传播属性(传播行为)可选值说明 (二)声明 ...

  6. Spring事务管理(三)-PlatformmTransactionManager解析和事务传播方式原理

    2019独角兽企业重金招聘Python工程师标准>>> Spring在事务管理时,对事务的处理做了极致的抽象,即PlatformTransactionManager.对事务的操作,简 ...

  7. Spring事务管理中异常回滚知识点总结

    记录总结Spring核心知识点:事务使用与它的传播机制 目录 前言 问题场景 Spring 管理事务的原理 MySQL中的事务管理 JDBC中的事务管理 Spring 中的事务管理 Spring中的事 ...

  8. Spring事务管理-》Spring事务管理(annotation)

    5.6 使用@Transactional 除了使用XML类型的事务管理,同时Spring也提供了Annotation类型的事务管理.如下所示: 一:Spring事务管理 =============== ...

  9. 浅入浅出代理模式与Spring事务管理

    本文回顾了代理模式和Spring事务管理的原理. 前言 最近在开发业务代码的时候,犯了一个事务注解的错误:在同一个类的非事务方法中调用了另一个事务方法,导致事务没有生效,如下所示: public Co ...

最新文章

  1. 2021-03-19注解是分功能模块的@ReuqestMapping注解需要依赖的jar为jar-web ,@Autowired注解依赖的是springframework包
  2. Retrofit 2.0:有史以来最大的改进
  3. Swift3中的 GCD
  4. python中列表用某个数字出现的次数_Python实现统计给定列表中指定数字出现次数的方法...
  5. jzoj100029. 【NOIP2017提高A组模拟7.8】陪审团(贪心,排序)
  6. MySQL教程(一)—— 数据库设计
  7. C# 对象与JSON串互相转换
  8. 阿里云与中国联通首个公共云平台上线
  9. 如何带领团队“攻城略地”?优秀的架构师这样做
  10. Element-UI Form表单 resetFields() 重置表单无效问题
  11. linux安装两个jdk_jdk在linux上安装过程
  12. linux 查看真实路径-软连接
  13. web app开发利器 - iscroll4 解决方案
  14. 单元测试-Mock Server
  15. 浅谈关于QQ核360的恩怨纠纷
  16. 电脑插上u盘计算机管理有显示,u盘在电脑上一直显示扫描怎么办
  17. oc引导windows蓝屏_电脑蓝屏你别怕,黑客教你代码查看问题
  18. IPv6安装及使用手册
  19. 【计算机网络】第九章:应用层
  20. 园区人工智能开启双创模式,“1+N”创新型组织发展成效初显

热门文章

  1. Glide无法使用任何Transform/Gilde使用Transform不生效问题说明以及Gilde加载任意角圆角实现
  2. ArcGIS水文分析实战教程(7)细说流域提取 1
  3. 核心概念 —— 契约(Contracts)
  4. python删除列索引_python中pandas.DataFrame的简单操作方法(创建、索引、增添与删除)...
  5. 争议 | VR 对眼睛害处到底大不大? 能不能给小孩玩?
  6. 最新jym在线客服源码系统
  7. AMD双核补丁吐血之作6个补丁
  8. IntelliJ IDEA 15款 神级超级牛逼插件推荐(自用,真的超级牛逼)
  9. linux cdc设备驱动,kernel linux 3.5使用USB CDC ACM驱动
  10. ╮(╯▽╰)╭发现好难( ⊙ o ⊙ )啊!