目录
注解声明
propagation (事务传播行为)
REQUIRED
外部没有事务则创建当前事务
外部有事务则合并(加入)事务编辑
REQUIRES_NEW
外部没有事务则创建一个事务
外部有事务则将外部事务挂起,内部影响外部,外部不影响内部
SUPPORTS
外部没有事务则以普通方法执行编辑
外部有事务则加入当前事务执行
NOT_SUPPORTED
外部没有事务则以普通方法执行
外部有事务则挂起外部事务,内部还是以普通方法执行,内部如果发生了报错也不会影响到外部的事务
MANDATORY
外部没有事务则抛出异常
外部有事务则加入当前事务执行
NEVER
外部没有事务则以普通方法执行
外部有事务则抛出异常
NESTED
外部没有事务就创建一个内部的事务
外部有事务则嵌套在外部事务中 , 外部影响内部,内部不影响外部
场景题
isolation(事务隔离级别)
并发事务引起的问题
脏读(Dirty reads)
不可重复读(Nonrepeatable read)
幻读(Phantom read)
不可重复读与幻读的区别
隔离级别
timeout
readOnly
rollbackFor 和 noRollbackFor
事务失效的场景
注解声明
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
//传播行为
Propagation propagation() default Propagation.REQUIRED;
//隔离级别
Isolation isolation() default Isolation.DEFAULT;
//超时级别
int timeout() default -1;
//是否只读
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
propagation (事务传播行为)
Spring中的事务传播机制用于控制多个事务方法相互调用时事务的行为,复杂的业务场景中,多个事务方法之间的调用可能会导致事务的不一致,比如数据丢失,重复提交等问题,使用事务传播机制可以避免这些问题的发生,保证事务的一致性和完整性
Spring的事务规定了7中事务的传播级别,默认的传播机制是REQUIRED
传播行为 | 含义 |
---|---|
REQUIRED (0) | 如果不存在事务则开启一个事务,如果存在事务则加入之前的事务,保证总是会有一个事务在执行 |
REQUIRES_NEW (3) | 每次执行都新开一个事务,如果当前存在事务,则把当前事务挂起 |
SUPPORTS (1) | 有事务就加入事务,没有则普通执行 |
NOT_SUPPORTED (4) | 有事务则暂停该事务,没有则普通执行 |
MANDATORY (2) | 强制有事务,没有事务则报异常 |
NEVER (5) | 有事务则报异常 |
NESTED (6) | 如果之前有事务,则创建嵌套事务,嵌套事务回滚不影响父事务,反之父事务则影响嵌套事务 |
PS : 下面说的“外部”指的A方法中调B方法,那A方法就是外部方法,B是内部方法
REQUIRED
外部没有事务则创建当前事务
外部有事务则合并(加入)事务
REQUIRES_NEW
外部没有事务则创建一个事务
只是内部的一个事务,不包括外部的
外部有事务则将外部事务挂起,内部影响外部,外部不影响内部
比如两个事务,A调B,挂起A事务,执行B事务,B事务提交或回滚后,恢复A事务执行
SUPPORTS
外部没有事务则以普通方法执行
外部有事务则加入当前事务执行
NOT_SUPPORTED
外部没有事务则以普通方法执行
外部有事务则挂起外部事务,内部还是以普通方法执行,内部如果发生了报错也不会影响到外部的事务
MANDATORY
外部没有事务则抛出异常
外部有事务则加入当前事务执行
NEVER
外部没有事务则以普通方法执行
外部有事务则抛出异常
NESTED
外部没有事务就创建一个内部的事务
外部有事务则嵌套在外部事务中 , 外部影响内部,内部不影响外部
PS : 这里需要注意下REQUIRES_NEW和NESTED的区别,正好是反过来了
REQUIRES_NEW : 内部影响外部,外部不影响内部
NESTED :内部不影响外部,外部影响内部
场景题
问:一个长的事务方法a,在读写分离的情况下,里面即有读库操作,也有写库的操作,再调用个读库方法b,方法该用什么传播机制呢?
这种情况,读方法如果是最后一步,直接not_supported就行了,避免读报错导致数据回滚,如果是中间步骤,最好还是required,因为异常失败需要回滚一下。
例如:A B C三个操作,C就是最后一步,B就是中间步骤。
如果一个读操作在中间(如B操作)失败了,那么就需要让A做回滚,因为C还没执行,所以A必须回滚才能保证一致性。
场景题来源:程序员hollis面试题库中
isolation(事务隔离级别)
事务的隔离级别定义了一个事务可能受其他并发事务影响的程度。
并发事务引起的问题
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下问题:
脏读(Dirty reads)
脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)
不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)
幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
不可重复读与幻读的区别
不可重复读的重点是修改 :
同样的条件, 你读取过的数据, 再次读取出来发现值不一样了
例如:在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
con1 = getConnection();
select salary from employee empId ="Mary";
在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
con2 = getConnection();
update employee set salary = 2000;
con2.commit();
在事务1中,Mary 再次读取自己的工资时,工资变为了2000
//con1
select salary from employee empId ="Mary";
在一个事务中前后两次读取的结果并不一致,导致了不可重复读。
幻读的重点在于新增或删除:
同样的条件, 第1次和第2次读出来的记录数不一样
例如:目前工资为1000的员工有10人。事务1,读取所有工资为1000的员工。
con1 = getConnection();
Select * from employee where salary = 1000;
共读取10条记录
这时另一个事务向employee表插入了一条员工记录,工资也为1000
con2 = getConnection();
Insert into employee(empId,salary) values("Lili",1000);
con2.commit();
事务1再次读取所有工资为1000的员工
//con1
select * from employee where salary =1000;
共读取到了11条记录,这就产生了幻像读。
从总的结果来看, 似乎不可重复读和幻读都表现为两次读取的结果不一致。
但如果你从控制的角度来看, 两者的区别就比较大。
对于前者, 只需要锁住满足条件的记录。 对于后者, 要锁住满足条件及其相近的记录。
隔离级别
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT (-1) | 默认,使用后端数据库默认的隔离级别 |
ISOLATION_READ_UNCOMMITTED (1) | 读未提交,最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读 |
ISOLATION_READ_COMMITTED (2) | 读已提交,允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生 |
ISOLATION_REPEATABLE_READ (4) | 可重复读,对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生 |
ISOLATION_SERIALIZABLE (8) | 串行化的,最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的 |
PS : 隔离级别越高,数据越安全,并发性能越差
timeout
指定事务超时时间,单位为秒。如果事务执行时间超过设定的超时时间,将回滚事务。
@Transactional(timeout = 30)
public void methodWithTimeout() {
// ...
}
readOnly
指定事务是否为只读,默认值为 false。如果设置为 true,表示只读事务,不允许有写操作。
@Transactional(readOnly = true)
public void readOnlyMethod() {
// ...
}
rollbackFor 和 noRollbackFor
指定在哪些异常情况下回滚事务或不回滚事务。
@Transactional(rollbackFor = CustomException.class)
public void methodWithRollbackForException() {
// ...
}
@Transactional(noRollbackFor = AnotherException.class)
public void methodWithNoRollbackForException() {
// ...
}
事务失效的场景
Spring中事务的失效场景-CSDN博客