目的:当一个方法内,有操作多张关联表数据的操作时,为保证数据的一致性,需要对这个方法开启事务。
- 当方法内有异常时,则会进行回滚,数据返回到事务操作前;
spring事务
的数据回滚依赖于数据库对事务的支持
事务特性:
- A -原子性 :事务内的数据修改操作要么都成功,要么都失败
- C -一致性 :事务内相关联数据的一致性
- I - 隔离型 :事务与事务之间的数据相互隔离互不影响
- D -持久性 : 事务正常结束对表数据的修改是持久的
事务间的数据隔离级别 :(级别由低->高)
READ_UNCOMMITTED
:读未提交
,在事务未commit
提交前,其它事务可以读取改事务数据READ_COMMITTED
:读提交
,在事务commit
提交后,其它事务才可以读取改事务数据REPEATABLE_READ
:可重复读取提交
,第一次进入事务时的查询的值,会一直保存,其它事务提交数据不影响当前事务,该事务读取的值还是之前的。SERIALIZABLE
:串行化
,在其它事务开启时,select会加锁,在其它事务执行查询时会阻塞等待,前一个事务提交完成
脏读
:
- 一个事务读取到了另一个事务未提交的数据操作结果
不可重复读
:
- 一个事务对同一行数据重复读取两次,但是却得到了不同的结果;在读取期间,其它事务提交事务修改了值
幻读
:
- 某个事务读取某范围内的数据时,另一个事务在这个范围内添加了新的记录会产生换行,如本来两条数据读出了3条;不满足可重复读。(默认 MVCC版本并发控制,会解决幻读,读已提交、可重复读是会在每行数据添加版本,提交时会比较版本号)
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大mysql
默认隔离级别:REPEATABLE_READoracle
默认隔离级别:READ_COMMITTED
在Spring中使用事务:
编程式事务实现(
细粒度
)
@Resource
private TransactionTemplate transactionTemplate;
@Override
public void one() {
//隔离级别、传播性设置
//transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
//transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
transactionTemplate.execute(status -> {
test1Mapper.updateByPrimaryKey(new Test1().setId(1).setStatus("1"));
int i=1/0;
test2Mapper.updateByPrimaryKey(new Test2().setId(1).setStatus("1"));
return true;
});
}
声明式事务实现 (
粗粒度
-方法级别)
@Override
@Transactional(rollbackFor = Throwable.class)
public void two() {
test1Mapper.updateByPrimaryKey(new Test1().setId(1).setStatus("1"));
int i=1/0;
test2Mapper.updateByPrimaryKey(new Test2().setId(1).setStatus("1"));
}
Spring事务
传播性
- 事务传播是指,一个事务方法开启事务而这个方法内嵌了另一个事务方法时,事务与事务之间的传播关系
默认:
Propagation.REQUIRED
,表示当前方法必须运行在事务中,如果没有则新建一个事务
案例一 :contain() 方法开启事务,内嵌方法 updateT3AndT4() 未开启事务,此时都会算在一个事务内,报错一起回滚,单独调用updateT3AndT4()不存在事务。
@Override
@Transactional(rollbackFor = Throwable.class)
public void contain() {
test1Mapper.updateByPrimaryKey(new Test1().setId(1).setStatus("1"));
test2Mapper.updateByPrimaryKey(new Test2().setId(1).setStatus("1"));
test2Service.updateT3AndT4();
}
@Override
//@Transactional(rollbackFor = Throwable.class) 效果一样
public void updateT3AndT4() {
test3Mapper.updateByPrimaryKey(new Test3().setId(1).setStatus("1"));
int i=1/0;
test4Mapper.updateByPrimaryKey(new Test4().setId(1).setStatus("1"));
}
默认:
Propagation.SUPPORTS
,表示当前不需要事务上下文,如果存在当前事务,那么该方法会在事务中运行
案例二:当前外层方法不是 propagation = Propagation.REQUIRED
级别时,因为如果是这级别,那里外都会在一个事务中,都会生效异常回滚。
@Override
@Transactional(rollbackFor = Throwable.class,propagation = Propagation.SUPPORTS)
public void contain() {
test1Mapper.updateByPrimaryKeySelective(new Test1().setId(1).setStatus("1"));
int i=1/0;
test2Mapper.updateByPrimaryKeySelective(new Test2().setId(1).setStatus("1"));
test2Service.updateT3AndT4();
}
@Override
@Transactional(rollbackFor = Throwable.class,propagation = Propagation.REQUIRED)
public void updateT3AndT4() {
test3Mapper.updateByPrimaryKeySelective(new Test3().setId(1).setStatus("1"));
int i=1/0;
test4Mapper.updateByPrimaryKeySelective(new Test4().setId(1).setStatus("1"));
}
外层方法未回滚,内存方法回滚了。
都为 Propagation.SUPPORTS 级别时👇
@Override
@Transactional(rollbackFor = Throwable.class,propagation = Propagation.SUPPORTS)
public void contain() {
test1Mapper.updateByPrimaryKeySelective(new Test1().setId(1).setStatus("1"));
int i=1/0;
test2Mapper.updateByPrimaryKeySelective(new Test2().setId(1).setStatus("1"));
test2Service.updateT3AndT4();
}
@Override
@Transactional(rollbackFor = Throwable.class,propagation = Propagation.SUPPORTS)
public void updateT3AndT4() {
test3Mapper.updateByPrimaryKeySelective(new Test3().setId(1).setStatus("1"));
int i=1/0;
test4Mapper.updateByPrimaryKeySelective(new Test4().setId(1).setStatus("1"));
}
内存事务回滚了。
实际使用上,一般都为 默认传播级别就可以了,因为需要开启事务时,会把关联表的操作放在同一个事务方法里。
- 可能在使用上,有时会选择更细粒度的,模板事务
TransactionTemplate
,因为方法中如果包含一些业务数据计算,是可以不包含在事务里面的,因为我们的目的基本是为了保持数据的一致性!