// 源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
String timeoutString() default "";
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
1. @Transactional 属性
value
value 属性与 transactionManager属性互为别名,用于指定事务管理器。当存在多个数据源时,往往需要不同的数据源,使用不同的事物管理器,可以使用value属性,或者transactionManager属性指定具体的事物管理器。
transactionManager
@see value
propagation
事务的传播行为
假设当前有两个方法 A, B。方法A 调用了 方法B, A与B有都具有事务,当A 调用B 后,B的事务该怎样变化呢,这个就取决于设置的事务传播行为。事实上 B 的事务,大致有三种情况:第一 开启新的事务;第二 加入 A的事务;第三 以非事务的方式运行;
以下的例子均为 A 调用 B
PROPAGATION_REQUIRED
:也是默认的事务传播行为。如果当前存在事务,则加入当前事务;如果当前不存在事务,则开启一个新的事务- 当前存在事务:A异常回滚时,B也会回滚;B异常回滚时,A也会回滚,同一个事务
- 当前不存在事务:即 A 没开启事务,B会单独开启一个事务,当B回滚时,不会影响A, 当然 A中出现异常,也不会影响B;
PROPAGATION_SUPPORTS
:如果当前存在事务,则加入当前事务(A,B同一个事务);如果当前不存在事务,则也不开启事务
PROPAGATION_MANDATORY
:如果当前存在事务,则加入当前事务 (同一个事务);如果当前不存在事务,则抛出异常
PROPAGATION_REQUIRES_NEW
:重新开启一个新事物;- 如果当前存在事务,当前事务对B不起作用,也就是当A发送异常时,只会回滚A中的数据,对B没有影响。但是如果B抛出了异常,且这个异常恰好满足A的回滚机制时,A会回滚。
- 如果当前不存在事务:显然B不会影响A, 因为A不具有事务;A也不会影响B, 因为A中的异常,B也没法捕获
PROPAGATION_NOT_SUPPORTED
:以非事务的方式运行,如果当前存在事务,当前事务只会对A生效,不会对B生效PROPAGATION_NEVER
:以非事务的方式运行,如果当前存在事务,抛出异常PROPAGATION_NESTED
如果当前存在事务,则会作为当前事务的嵌套事务来继续运行。如果当前不存在事务,则会开启一个新事务。- 当前存在事务:当A 回滚时,B也会回滚;但是当B回滚时,不会影响A
- 当前不存在事务:B会开启一个新事物,这个事务只作用与B, B回滚不会影响A
isolation
事务的隔离级别
事务的隔离级别即在多并发的情况下,多个事务之间的隔离程度
Isolation.DEFAULT
使用数据库默认的隔离级别。mysql 默认可重复读;oracle默认读已提交Isolation.READ_UNCOMMITTED
读未提交- 允许读取还没提交的数据变更;这就可能造成脏读,幻读,不可重复读
- 脏读:A 先修改了 table中的某条数据中的某个属性的值,但是还未提交数据,此时 B 同样查询了该属性值,就会导致 B查询出的是无用的数据。
- 幻读:A读取了table中的一些数据,B在table中添加了一些数据,当A再次用相同的条件读取table中的数据,就会导致新查询出的数据多出几行,就像出现幻觉一样
- 不可重复读:A 查询了table中的一些数据,B对这些数据进行了修改。当A 再次查询这些数据的时候,会导致两次查询出的数据不一样,导致不可重复读到原始的数据
Isolation.READ_COMMITTED
读以提交;可以避免脏读,但是幻读和不可重复读依然可能发生Isolation.REPEATABLE_READ
可重复读;可以避免脏读和不可重复读,但是幻读可能发生Isolation.SERIALIZABLE
序列化;可以避免脏读,幻读,不可重复读。但是严重影响程序性能
timeout
事务超时时间;即当事务运行时间超过 timeout时,会自动回滚事务,默认值为 -1 ,不设置超时时间
readOnly
只读;只允许事务进行查询操作,对于一次执行多条查询语句的,可以开启readOnly,可以保证整体读一致性。默认false
rollbackFor
事务回滚规则,接收指定异常进行回滚
- 指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
- 指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName
事务回滚规则
- 指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”)
- 指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”})
noRollbackFor
@see rollbackFor
noRollbackForName
@see rollbackForClassName
2. @Transactonal 作用范围
- 作用在类上:Transactional 对该类下所有public方法都生效
- 作用在 public方法上。但必须是 public修饰的方法
- 接口上;不推荐使用
3. @Transactional 失效的情况
- 被@Transactional 修饰的方法所在的类,没有被spring所管理。
- @Transactional 作用在 非 public方法上
- @Transactional 的事务传播行为设置错误
- @Transactional 的 rollbackFor属性设置错误
- 同一个类中调用的方法调用 @Transactional 修饰的方法。方法 A,B 属于同一个类,方法A 没有被 @Transactional 修饰,方法 B 被 @Transactional 修饰。 在 A 中调用 B。当外部的方法再调用A时,事务可能会失效。尽量将 @Transactional 修饰的方法抽离出来
- 异常被 catch 抓住,导致@Transactional 无法捕获异常
- 数据库引擎不支持事务