前情提要

在编写业务层方法时,会遇到很多需要事务提交的操作,spring框架为我们提供很方便的做法,就是在需要事务提交的方法上添加@Transactional注解,比起我们自己开启事务、提交以及控制回滚,要简单的多。但是在使用的时候容易犯一些错误。我就自己的错误经历总结如下。

枯燥的背景知识(可以忽略)

编程式事务管理&声明式事务管理:

(一)编程式事务管理 在 Spring 出现以前,编程式事务管理对基于 POJO 的应用来说是唯一选择。用过 Hibernate 的人都知道,我们需要在代码中显式调用beginTransaction()、commit()、rollback()等事务管理相关的方法,这就是编程式事务管理。通过 Spring 提供的事务管理 API,我们可以在代码中灵活控制事务的执行。在底层,Spring 仍然将事务操作委托给底层的持久化框架来执行。

(二)声明式事务管理

1)Spring 的声明式事务管理在底层是建立在 AOP 的基础之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

2)声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过等价的基于标注的方式),便可以将事务规则应用到业务逻辑中。因为事务管理本身就是一个典型的横切逻辑,正是 AOP 的用武之地。Spring 开发团队也意识到了这一点,为声明式事务提供了简单而强大的支持。

3)声明式事务管理曾经是 EJB 引以为傲的一个亮点, Spring 让 POJO 在事务管理方面也拥有了和 EJB 一样的待遇,让开发人员在 EJB 容器之外也用上了强大的声明式事务管理功能,这主要得益于 Spring 依赖注入容器和 Spring AOP 的支持。依赖注入容器为声明式事务管理提供了基础设施,使得 Bean 对于 Spring 框架而言是可管理的;而 Spring AOP 则是声明式事务管理的直接实现者。

4)建议在开发中使用声明式事务,不仅因为其简单,更主要是因为这样使得纯业务代码不被污染,极大方便后期的代码维护。和编程式事务相比,声明式事务唯一不足地方是,后者的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。


从@Transactional的代码当中可以看到,可配置的参数主要有:

  1. 事务隔离级别: Isolation isolation() default Isolation.DEFAULT;
  2. 事务传播行为:Propagation propagation() default Propagation.REQUIRED;
  3. 回滚、不回滚的异常类:rollbackFor() noRollbackFor()
  4. 超时:int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

mysql的innob引擎支持标准sql事务的四个级别:

REPEATABLE READ(默认)

| READ COMMITTED

| READ UNCOMMITTED

| SERIALIZABLE

默认是可以重复读,以repeatable_read 为例,默认情况它使用了Consistent Nonlocking Reads,其实就是多版本快照方式,一个repeatable_read 事务里面,第一次读取从数据库取,然后这个数据放入快照里面,后面的读取都是从快照里面取,这样虽然保证了一个事务里面读取的数据的一致性,但是会出现其他事务已经改了数据库里面的值,而当前事务却还在使用老的数据。如果对于数据的同步要求很高的话,可以在sql上面使用锁,

select … lock in share mode; //共享锁,可同时读,不能同时写

select … for update; // 排他锁,独占

但是用锁,肯定影响性能,至少mysql没有使用锁来实现repeatable-read隔离级别。

如果是SERIALIZABLE级别,mysql倒是会自动的将select转化为select … LOCK IN SHARE MODE,即使用了共享锁。


事务的传播行为: 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:

1、TransactionDefinition.PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。

2、TransactionDefinition.PROPAGATION_REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

3、TransactionDefinition.PROPAGATION_SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

4、TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

5、TransactionDefinition.PROPAGATION_NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

6、TransactionDefinition.PROPAGATION_MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

7、TransactionDefinition.PROPAGATION_NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。


总结:

  1. Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。
  2. “proxy-target-class” 属性值来控制是基于接口的还是基于类的代理被创建。 <tx:annotation-driven transaction-manager=“transactionManager” proxy-target-class=“true”/> 注意:proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理。
  3. 注解@Transactional cglib与java动态代理最大区别是代理目标对象不用实现接口,那么注解要是写到接口方法上,要是使用cglib代理,这是注解事物就失效了,为了保持兼容注解最好都写到实现类方法上。
  4. Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。
  5. @Transactional 的事务开启 ,或者是基于接口的 或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。 原因:(这也是为什么在项目中有些@Async并没有异步执行) spring 在扫描bean的时候会扫描方法上是否包含@Transactional注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类,proxy),代理类是继承原来那个bean的。此时,当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个bean,所以就不会启动transaction,我们看到的现象就是@Transactional注解无效。

Tips: 使用 ~for update ~实现实现悲观锁的时候,需要注意锁的级别,Mysql InnoDB 默认行级锁。行级锁都是基于索引的,如果一条sql语句用不到索引,是不会使用行级锁的,会使用表级,把整张表锁住,这点需要注意。

Tips: 使用乐观锁时多数实现方法是使用版本号,或者时间戳。但是如果事务的隔离级别允许重复读(比如:REPEATABLE_READ;mysql InnoDB默认也是这个级别),那么使用乐观锁是查询不出版本或者时间戳的变化的,但是oracle的话默认就可以。

Tips: spring默认的事务隔离级别为底层数据区的隔离级别。所以,如果你用的是Mysql的InnoDB引擎,那么级别就是:REPEATABLE READ;如果你用的是oracle,那么级别就是READ COMMITED。

Tips: spring的@Transactional注解事务创博行为默认值为:PROPAGATION_REQUIRED

以上是我能想到的相关,如有错误希望大家及时和我探讨。

相关查询资料:

[http://zliguo.iteye.com/blog/2230013]

[http://my.oschina.net/guanzhenxing/blog/214228]

[http://blog.csdn.net/u012228718/article/details/42750119#t1]

[http://my.oschina.net/yybear/blog/103235]

[http://my.oschina.net/feichexia/blog/202520]

[http://www.hollischuang.com/archives/934]

[http://blog.csdn.net/clementad/article/details/47339519]

from: http://mojito515.github.io/blog/2016/08/31/transactionalinspring/

使用spring中的@Transactional注解时,可能需要注意的地方的更多相关文章

  1. [转]使用spring中的@Transactional注解时,可能需要注意的地方

    前情提要 在编写业务层方法时,会遇到很多需要事务提交的操作,spring框架为我们提供很方便的做法,就是在需要事务提交的方法上添加@Transactional注解,比起我们自己开启事务.提交以及控制回 ...

  2. 对于spring中事务@Transactional注解的理解

    现在spring的配置都喜欢用注解,这边就说下@Transactional 一.如何开启@Transactional支持 要使用@Transactional,spring的配置文件applicatio ...

  3. spring中的@Transactional注解

    前几天灿哥问我,在做程序的时候,有没有考虑到事务,如果一个函数在中间执行过程中报错了,它会回滚么?我查了一查,spring确实有这样一个注解,能快速帮助我们配置事务管理.下面我就简单介绍一下这个注解. ...

  4. Spring中的@Transactional

    spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题. 一般使用是通过如下代码对方法或接口或类注释: @Transactiona ...

  5. spring 事务-使用@Transactional 注解(事务隔离级别)

    转: spring 事务-使用@Transactional 注解(事务隔离级别) 2016年08月11日 21:49:20 华华鱼 阅读数 15490 标签: spring事务Transactiona ...

  6. Spring下面的@Transactional注解的讲解

    摘自: https://www.cnblogs.com/xiohao/p/4808088.html Spring下面的@Transactional注解标志的讲解 最近在开发中对Spring中的事务标记 ...

  7. Spring中的@Transactional必须要了解的概念

    spring中的@Transactional基于动态代理的机制,提供了一种透明的事务管理机制,方便快捷解决在开发中碰到的问题. 一般使用是通过如下代码对方法或接口或类注释: 1 @Transactio ...

  8. Spring 中aop切面注解实现

    spring中aop的注解实现方式简单实例   上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了. 注解方式实现aop我们 ...

  9. Spring中的常用注解

    Spring中的常用注解 1.@Controller 标识一个该类是Spring MVC controller处理器,用来创建处理http请求的对象.

随机推荐

  1. USACO 5.5 Twofive

    TwofiveIOI 2001 In order to teach her young calvess the order of the letters in the alphabet, Bessie ...

  2. MyEclipse个性化设置

    1.修改项目文件默认编码 Note:myEclipse默认的编码是GBK, 也就是未设置编码格式的文件都默认使用GBK进行编码, 而更糟糕的是JSP.JavaScriptt默认编码竟然是ISO-885 ...

  3. css盒子垂直居中

    首先父盒子包住子盒子 <body> <div class="outbox"> <div class="box"></d ...

  4. 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]

    题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...

  5. react篇章-事件处理

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title&g ...

  6. 深入理解SQL的四种连接,左外连接,右外连接,内连接,全连接

    1.内联接(典型的联接运算,使用像 =  或 <> 之类的比较运算符).包括相等联接和自然联接.     内联接使用比较运算符根据每个表共有的列的值匹配两个表中的行.例如,检索 stude ...

  7. python修改文件的属性

    1.执行attrib系统命令 ATTRIB [+R | -R] [+A | -A ] [+S | -S] [+H | -H] [+I | -I] [drive:][path][filename] [/ ...

  8. 说一说Servlet的生命周期

    servlet有良好的生存期的定义,包括加载和实例化.初始化.处理请求以及服务结束.这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达. Se ...

  9. requests https访问错误SSLError: certificate verify failed 及InsecureRequestWarning处理办法

    转自: https://blog.csdn.net/mighty13/article/details/78076258?locationNum=3&fps=1 在使用requests访问某网站 ...

  10. 最短网络Agri-Net

    [问题描述] 农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场.当然,他需要你的帮助.约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场. ...