@Transactional

一、作用于接口、接口方法、类以及类方法上

1️⃣当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性。

2️⃣当作用在方法级别时会覆盖类级别的定义。

3️⃣当作用在接口和接口方法时则只有在使用基于接口的代理时它才会生效,也就是 JDK 动态代理,而不是 Cglib 代理。如果正在使用基于类的代理(也就是 CGLIB 代理)时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装,因为注解是不能继承的。
Spring 的建议是在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。

4️⃣当在 protected、private 或者默认可见性的方法上使用 @Transactional 时是不会生效的,也不会抛出任何异常。

5️⃣默认情况下,只有来自外部的方法调用才会被 AOP 代理捕获,也就是,类内部方法调用本类内部的其他方法并不会引起事务行为,即使被调用方法使用 @Transactional 进行修饰。

二、@Transactional 配置事务失效的场景

1️⃣@Transactional 应用在非 public 修饰的方法上。
注意:protected、private 修饰的方法上使用 @Transactional,虽然事务无效,但不会有任何报错。

2️⃣@Transactional 属性 propagation 设置错误。
这种失效是由于配置错误,若是错误的配置以下三种 propagation,事务将不会发生回滚:
@Transactional(propagation=Propagation.SUPPORTS):如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 ②@Transactional(propagation=Propagation.NOT_SUPPORTED):以非事务方式运行,如果当前存在事务,则把当前事务挂起。
@Transactional(propagation=Propagation.NEVER):以非事务方式运行,如果当前存在事务,则抛出异常。

3️⃣@Transactional 属性 rollbackFor 设置错误。
rollbackFor 可以指定能够触发事务回滚的异常类型。Spring 默认抛出了未检查 unchecked 异常(继承自 RuntimeException 的异常)或者 Error 才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor 属性。

4️⃣同一个类中方法调用,导致 @Transactional 失效。
一个类 Test,它的方法 A(未声明注解事务),调用本类的方法 B(声明有注解事务。不论是 public 的还是 private 的)。外部调用方法 A 之后,方法 B 的事务是不会起作用的。

5️⃣如果异常被try{}catch{}了,事务就不回滚。如果想让事务回滚必须再往外抛try{}catch{throw new RunTimeException()}

如果 B 方法内部抛了异常,而 A 方法此时 try catch 了 B 方法的异常,该事务不能正常回滚。会抛出异常:

org.springframework.transaction.UnexpectedRollbackException: 
Transaction rolled back because it has been marked as rollback-only

因为当 B 中抛出了一个异常以后,B 标识当前事务需要 rollback。但是由于 A 手动捕获该异常并进行处理,A 认为当前事务应该正常 commit。此时就出现了前后不一致,也就是因为这样,抛出了前面的UnexpectedRollbackException

Spring 的事务是在调用业务方法之前开始的,业务方法执行完毕之后才执行 commit/rollback,事务是否执行取决于是否抛出 RuntimeException。如果抛出 RuntimeException,并在业务方法中没有 catch 的话,事务会回滚。

在业务方法中一般不需要 catch 异常,如果非要 catch 一定要throw new RuntimeException(),或者注解中指定抛异常类型@Transactional(rollbackFor = Exception.class),否则会导致事务失效,数据 commit 造成数据不一致,所以有些时候 try catch 反倒会画蛇添足。

6️⃣数据库引擎不支持事务
事务能否生效,数据库引擎是否支持事务是关键。常用的 MySQL 数据库默认使用支持事务的 innodb 引擎。一旦数据库引擎切换成不支持事务的 MyISAM,那事务就从根本上失效了。

三、@Transactional 的可用参数

1️⃣readOnly
该属性用于设置当前事务是否为只读事务,设置为 true 表示只读,false 则表示可读写,默认值为 false。

理解:
如果一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一致性。
如果一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保证整体的读一致性。否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。
【注意是一次执行多次查询来统计某些信息,这时为了保证数据整体的一致性,要用只读事务】

2️⃣rollbackFor
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:

  1. 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
  2. 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class,BusinessException.class})

3️⃣rollbackForClassName
该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:

  1. 指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")
  2. 指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","BusnessException"})

4️⃣noRollbackFor
该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。

5️⃣timeout
该属性用于设置事务的超时秒数,默认值为 -1,表示永不超时。

6️⃣propagation
该属性用于设置事务传播行为。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED)

四、基于注解@Transactional的事务

Spring 的事务基础架构代码将默认地只在抛出 RuntimeException 和 unchecked exceptions 时才标识事务回滚。也就是说,当抛出 RuntimeException 或其子类例的实例时(error 也一样)默认标识事务回滚。从事务方法中抛出的 Checked exceptions 将不被标识进行事务回滚。

1️⃣@Transactional 的异常控制,默认是 unChecked Exception 回滚;Checked Exception 不回滚。
2️⃣如果配置了rollbackFor 和 noRollbackFor 且两个都是用同样的异常,那么遇到该异常,还是回滚。
3️⃣rollbackFor 和 noRollbackFor 配置也许不会涵盖所有异常,对于遗漏的按照 unChecked Exception 回滚,Check ed Exception 不回滚。
4️⃣让 checked 例外也回滚:
在整个方法前加上@Transactional(rollbackFor=Exception.class)
5️⃣让 unchecked 例外不回滚:
@Transactional(notRollbackFor=RunTimeException.class)
6️⃣不需要事务管理的(只查询的)方法:
@Transactional(propagation=Propagation.NOT_SUPPORTED)

注意:
1️⃣@Transactional 标识的方法,处理过程尽量的简单。尤其是带锁的事务方法,能不放在事务里面的最好不要放在事务里面。可以将常规的数据库查询操作放在事务前面进行,而事务内进行增、删、改、加锁查询等操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351