19、@Transactional的详解
一、不存在事务管理,发生异常不会回滚
二、@Transactional的事务传播级别:
事务传播级别 |
详解 |
REQUIRED |
加入事务,默认值 |
SUPPORTS |
存在就加入,不存在,非事务执行 |
MANDATORY |
存在就加入,不存在抛异常 |
REQUIRES_NEW |
重新新建事务,如果当前存在事务,则把当前事务挂起 |
NOT_SUPPORTED |
非事务执行,如果当前存在事务,就把当前事务挂起 |
NEVER |
不存在,非事务执行,如果当前存在事务,则抛出异常 |
NESTED |
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果不存在,则新建事务 |
三、@Transactional事务失效的情况:
@Transactional的本质是AOP
1、未配置事务管理器:
如果没有配置正确的事务管理器,或者事务管理器配置错误,@Transactional注解将会失效。确保在Spring配置文件中正确地配置了事务管理器。
2、方法未被Spring管理:
如果@Transactional注解的方法不是由Spring容器管理的(例如,通过new关键字手动创建对象),那么事务管理将不会生效。确保被注解的方法是Spring容器管理的Bean
3、方法被内部调用:
如果@Transactional注解的方法被同一个类中的另一个方法调用,而不是通过代理对象调用,那么事务将不会生效。这是因为Spring使用代理机制来实现事务,只有通过代理对象调用的方法才会被事务管理器拦截。
4、异常被捕获并处理:
如果被@Transactional注解的方法内部发生异常,并且异常被方法内部捕获并处理,那么事务将不会回滚。通常情况下,事务应该在发生异常时回滚,以确保数据的一致性和完整性。
5、非public方法:
如果@Transactional注解的方法是非public的,事务也可能会失效。在默认情况下,Spring只会代理public方法。
6、AOP顺序问题:
如果@Transactional注解的方法在AOP切面中的顺序不正确,也可能导致事务失效。确保@Transactional注解所在的切面在AOP代理链中的顺序正确
四、@Transactional在默认的事务传播级别下的方法调用:
A方法调B方法
1、A方法添加@Transactional注解,B方法未添加@Transactional注解:
(1)、A方法与B方法在同类中:
无论A方法还是B方法发生异常:AB方法均回滚
(2)、A方法与B方法不在同类中
无论A方法还是B方法发生异常:AB方法均回滚
2、A方法未添加@Transactional注解,B方法添加@Transactional注解:
(1)、A方法与B方法在同类中:
无论A方法还是B方法发生异常:AB方法均未回滚
(2)、A方法与B方法不在同类中
A方法发生异常:AB方法均未回滚
B方法发生异常:仅B方法回滚,A方法未回滚
3、总结:
(1)、如果A加@Transactional注解,不管是不是在一个类中,不管B加不加注解,AB都是在同一事务中;
(2)、如果A不加@Transactional注解,只有B加@Transactional注解,AB方法为同一类,事务失效;AB不同类,只有B有事务;
(3)、如果A不加@Transactional注解,B不加@Transactional注解,则没有事务;
注:
原因:A方法上有@Transactional注解,spring在管理的时候,会生成一个代理类,真正调用到A方法时,实际执行的是代理类里面的方法,该代理类里面的方法已经包括了B方法的调用,已经成为了一个方法。所以事务是有效的。
五、TransactionSynchronizationManager用于事务提交/回滚前后管理:
TransactionSynchronizationManager
是 Spring 框架中一个重要的类,用于管理事务同步资源。在使用 Spring 进行事务管理时,特别是在多线程环境或涉及多个资源(例如数据库连接、消息队列等)的复杂事务场景中,TransactionSynchronizationManager
可以帮助协调这些资源,使它们在一个事务上下文中得到正确的管理和处理。
主要功能:
- 事务资源绑定: 可以将特定的资源(如数据库连接、Hibernate会话等)绑定到当前事务上下文中。
- 事务同步: 提供在事务生命周期的各个阶段(如提交前、提交后、回滚前等)进行同步回调的机制。
- 事务状态管理: 管理和检查当前线程是否处于事务上下文中,并获取相关事务状态信息。
操作示例:
@Transactional public void TransactionManagerDemo(){ // 执行数据库操作逻辑 // 注册事务同步回调 // 提供在事务生命周期的各个阶段(如提交前、提交后、回滚前等)进行同步回调的机制 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { // 在事务提交后执行的逻辑 // 可以进行一些清理工作或发送通知 } @Override public void afterCompletion(int status) { // 在事务完成后执行的逻辑 // status表示事务的状态,可以根据不同状态执行不同的操作 //————STATUS_COMMITTED:表示事务已成功提交。 //————STATUS_ROLLED_BACK:表示事务已回滚。 switch (status) { case STATUS_COMMITTED: System.out.println("Transaction committed successfully."); // 执行事务提交后的逻辑 break; case STATUS_ROLLED_BACK: System.out.println("Transaction rolled back."); // 执行事务回滚后的逻辑 break; default: throw new IllegalStateException("Unexpected transaction status: " + status); } } @Override public void beforeCommit(boolean readOnly) { // 在事务提交前执行的逻辑 // readOnly表示事务是否为只读 //readOnly = true:标记事务为只读事务。这意味着在这个事务中不应该进行任何更改数据库状态的操作 @Transactional(readOnly = true) //readOnly = false(默认值):标记事务为可读写事务,这意味着可以进行数据的插入、更新或删除操作 @Transactional if (readOnly) { System.out.println("Before commit: This is a read-only transaction."); } else { System.out.println("Before commit: This is a read-write transaction."); } } @Override public void beforeCompletion() { // 在事务完成前执行的逻辑 } }); }
六、分布式事务管理:
Spring框架提供@Transactional注解就能进行相应的事务控制,但是由于每个服务(单体)内部的数据一致性是由本地事务来保证的,对于全局的数据一致性却没办法保证,总而言之,一次业务操作需要跨多个数据源或需要跨多个服务进行远程调用,就会产生分布式事务问题。一般使用Seata默认的AT模式解决分布式事务,AT模式牺牲了一致性,保证了可用性,不过,它保证的是最终一致性