@Transactional
直接在想要启动事务的方法或者类上添加@Transactional注解即可,在类上添加注解,默认类下的所有方法都会使用事务。
在类上添加注解
@Transactional
@Service
public class UserServiceImpl implements UserService {
}
在方法上添加注解
@Transactional
@Override
public User create() {}
为何使用事务
我们在修改数据库中的数据时,经常会一次修改多个表的数据,会有多条写操作,如果其中某一条执行失败了,已经执行的也不该生效,就会用到事务。
举个例子:假如一个电商平台,我要买一个东西,那么我们会创建订单,修改商品库存操作,如果创建了订单但是修改库存失败,或者修改库存成功,但是创建订单失败,这都是不能接受的,这两个操作是原子性的,成功就都成功,失败就都失败。这个时候就要用到数据库的事务了,在修改数据库之前创建事务,然后创建订单,修改库存,如果都成功了,在提交事务 ,否则事务要回滚。保持修改之前的状态。这样就保证了一致性。
事务可能引起的问题
脏读
A修改了某个数据,事务还没有提交,B读取了这条数据,虽然A改了,但是因为没提交,导致B读到的数据还是没改之前的数据。
不可重复读
B读取了一条数据,A修改了这条数据之后提交了,B第二次读取的时候跟第一次读取的数据不一致。
幻读
A删除了某条数据,在没提交事务之前,B读取了这条数据。
事务的参数配置
事务的隔离性isolation
DEFAULT :默认值(也是SpringBoot的隔离级别默认值),表示使用底层数据库的默认隔离级别。大部分数据库为READ_COMMITTED(MySql默认隔离级别为REPEATABLE_READ)
READ_UNCOMMITTED :该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。
READ_COMMITTED :该隔离级别表示一个事务只能读取另一个事务已经提交的数据。
REPEATABLE_READ :该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。
SERIALIZABLE :所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事务的传播性propagation
PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。
PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行
PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRESNEW:新建事务,如果当前存在事务,把当前事务挂起。也就是说业务方法被调用时,不管是否已经存在事务,业务方法总会为自己发起一个新的事务。如果调用业务方法的行为(方法)已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到业务方法执行结束,新事务才算结束,原先的事务才会恢复执行。
PROPAGATION_NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。 如果没有活动事务, 则按REQUIRED属性执行。
遇到异常事务回滚rollbackFor/noRollbackFor
当代码抛出异常的时候,事务将自动回滚,可以配置什么样的异常回滚,什么样的异常不回滚。
@Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class}, noRollbackFor = {IOException.class})
使用注意事项
同一个类中一个非事务性方法调用一个事务性方法,事务无效。
解决办法:可以跨类调用事务性方法,或者制作一个代理对象来调用本类方法。