事务是一个逻辑的工作单元,在这个工作单元中,如果存在有多个dml语句的情况,不允许部分执行成功,要么全部成功,要不全部失败,部分成功的语句,在某一个失败的情况下,也必须进行回滚,保证数据的完整性和一致性。
为什么要引入事务?
例如在转账的情况下,如果转入成功,转出失败,就会造成银行数据不一致,损失金钱,所以要么全都成功,要不全部回滚。
再例如,生成订单的时候,如果订单成功,订单明细插入失败,那么订单和订单明细数据就无法对应,订单数据错乱,后果不堪设想。
事务的特性:
事务代码
说明:注解事务非常简单,只需要一个@Transactional注解 即可
/**
* 转账
* @param id 进id
* @param id2 出id
* @param money
*/
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,rollbackFor= Exception.class,readOnly = false )
public void transfer(Integer id ,Integer id2 ,Integer money){
try{
userinfoMapper.add(id,money);
userinfoMapper.minus(id2,money);
}catch (Exception ex){
throw ex;
}
}
参数说明:
Spring 事务的传播机制
Spring 事务传播机制定义了多个包含了事务的⽅法,相互调⽤时,事务是如何在这些⽅法间进⾏传递的。
Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加⼊该事务;如果当前没有事务,则创建⼀个新的事务。
Propagation.SUPPORTS:如果当前存在事务,则加⼊该事务;如果当前没有事务,则以⾮事务的⽅式继续运⾏。
Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加⼊该事务;如果当前没有事务,则抛出异常。
Propagation.REQUIRES_NEW:表示创建⼀个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部⽅法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部⽅法会新开启⾃⼰的事务,且开启的事务相互独⽴,互不⼲扰。
Propagation.NOT_SUPPORTED:以⾮事务⽅式运⾏,如果当前存在事务,则把当前事务挂起。
Propagation.NEVER:以⾮事务⽅式运⾏,如果当前存在事务,则抛出异常。
Propagation.NESTED:如果当前存在事务,则创建⼀个事务作为当前事务的嵌套事务来运⾏;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED。
Spring 中设置事务隔离级别
Isolation.DEFAULT:以连接的数据库的事务隔离级别为主。
Isolation.READ_UNCOMMITTED:读未提交,可以读取到未提交的事务,存在脏读。
Isolation.READ_COMMITTED:读已提交,只能读取到已经提交的事务,解决了脏读,存在不可重复读。
Isolation.REPEATABLE_READ:可重复读,解决了不可重复读,但存在幻读(MySQL默认级别)。
Isolation.SERIALIZABLE:串⾏化,可以解决所有并发问题,但性能太低。
其中的一些概念的说明:
脏读: 指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据, 那么另外一 个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
不可重复读: 指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。 那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
幻读:指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及 到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,就会发生操作第一个事务的用户发现表中还有 没有修改的数据行,也就是说幻像读是指同一查询在同一事务中多次进行,由于其他提交事务所做的插入操作,每次返回不同的结果集,此时发生幻像读,就好象发生了幻觉一样。