@Transactional
一、技术名称
声明式注解@Transactional
二、定义
@Transactional注解是Spring框架中用于声明式事务管理的关键注解,主要作用于回滚。
事务四个特性:
「原子性 」表示一个事务中的所有操作要么全部成功执行,要么全部失败回滚;
「一致性 」表示事务执行前后数据库的状态保持一致;
「隔离性 」表示多个事务并发执行时,彼此之间是相互隔离的,互不干扰;
「持久性 」表示一旦事务提交,其所做的修改将永久保存在数据库中。
三、功能
将此注解放到类或方法上,当出现异常时可回滚sql,保证业务安全
1、事务的隔离级别:@Transactional中通过 isolation = Isolation.XXXX 进行设置。
是数据库系统用来定义事务在并发执行时的可见性和顺序的一组规则,用于解决并发事务之间可能出现的各种并发问题,如脏读、不可重复读、幻读等。
常见的隔离级别:
读未提交(Read Uncommitted)。这是最低的隔离级别,在这个级别下,事务可以读取到其他事务未提交的数据,可能会导致脏读、不可重复读和幻读等问题。
读已提交(Read Committed)。在这个级别下,事务只能读取已经提交的数据,这样可以避免脏读问题,但仍然可能存在不可重复读和幻读问题。
可重复读(Repeatable Read)。这个级别可以保证在一个事务内多次读取相同的数据时,数据不会被其他事务修改,这可以避免脏读和不可重复读问题,但仍然可能存在幻读问题。
串行化(Serializable)。这是最高的隔离级别,在这个级别下,事务会按顺序一个接一个地执行,不允许并发执行。这样可以避免脏读、不可重复读和幻读等并发问题,但会降低系统的并发性能。
2、事务的传播行为:@Transactional(propagation = Propagation.XXXX),
为了解决事务方法互相调用的事务问题。
Propagation.REQUIRED(默认传播行为):如果父方法有事务,就加入父方法事务,如果没有就新建自己独立的事务!
Propagation.REQUIRES_NEW:不管父方法有没有事务,都自己新建事务!也就是互不影响!
Propagation.NESTED:当前事务回滚,父事务不回滚,但父事务回滚,当前事务一定回滚!
3、事务的回滚:
回滚:@Transactional中通过 rollbackFor = xxxException.class
不回滚:noRollbackFor属性:指定哪些异常不会回滚, 默认没有指定
4、事务超时则回滚:
超时了多半是遇到某些问题,导致程序卡住,从而长时间占用数据库资源,设置超时及时释放资源。
超时时间在@Transactional中通过 timeout进行设置。
5、事务设置只读:
对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。
// readOnly = true把当前事务设置为只读 默认是false!
@Transactional(readOnly = true)
四、使用场景
当一个方法内有多条操作数据库的操作
事务失效场景:
1、被标注的方法的修饰符为非public
2、当前类中,使用非事务方法调用事务方法
3、事务方法内部捕捉了异常,没有异常抛出方法外
4、未指定能触发回滚的异常类型
默认RuntimeException或者Error才回滚异常
五、底层
public class TransactionalAnnotationProcessor { private PlatformTransactionManager transactionManager;
public TransactionalAnnotationProcessor(PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; }
public Object process(Object target, Method method, Object[] args) throws Throwable { Transactional transactional = method.getAnnotation(Transactional.class); TransactionStatus transactionStatus = null;
try { // 开启事务 transactionStatus = transactionManager.getTransaction(new DefaultTransactionDefinition());
// 执行目标方法 Object result = method.invoke(target, args);
// 提交事务 transactionManager.commit(transactionStatus);
return result; } catch (Exception ex) { // 回滚事务 if (transactionStatus != null) { transactionManager.rollback(transactionStatus); }
throw ex; } } } |
TransactionalAnnotationProcessor类是一个简化版本的注解解析器。它接收一个PlatformTransactionManager对象作为构造函数的参数,用于管理事务。process()方法接收目标对象、目标方法和方法参数作为参数。该方法首先通过反射获取目标方法上的@Transactional注解,然后根据注解的配置开启事务。接着,执行目标方法,并根据方法执行的结果决定是提交事务还是回滚事务。
六、其它
1、脏读:
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
2、幻读:
数据行变多或者变少了。事务 A 根据条件查询得到了 N 条数据,但此时事务 B 删除或者增加了 M 条符合事务 A 查询条件的数据,这样当事务 A 再次进行查询的时候真实的数据集已经发生了变化,但是A却查询不出来这种变化,因此产生了幻读。
3、不可重复读:
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
不可重复读和幻读的区别:
不可重复读强调的是数据行数据发生改变。
幻读强调的是结果集发生改变。
引用文章: