一、非public修饰的方法
@Transactional注解只能在在public修饰的方法下使用。
/*** 私有方法上的注解,不生效(因私有方法Spring扫描不到该方法,所以无法生成代理)*/
@Transactional
private boolean test() {//test code
}
非public修饰的方法事务失效的原因主要有以下几点:
1. 访问权限限制:非public修饰的方法可能无法被外部调用,而事务通常需要在外部调用的方法中开启。如果方法无法被外部调用,事务就无法生效。
2. 事务管理器配置问题:事务管理器可能没有正确配置非public方法的事务传播行为。事务传播行为定义了事务在方法调用链中的传播方式,包括事务的开启、提交和回滚等操作。如果事务管理器没有正确配置非public方法的传播行为,事务可能无法正常生效。
3. 代理机制问题:事务通常是通过代理机制来实现的,代理对象会拦截方法调用,并在方法调用前后进行事务管理。但是非public方法可能无法被代理对象拦截,导致事务无法生效。
4. 异常处理问题:事务通常依赖于异常的抛出和捕获来进行事务的回滚或提交。但是非public方法可能无法抛出或捕获异常,导致事务无法正确处理异常情况。
总之,非public修饰的方法事务失效的原因主要是因为访问权限限制、事务管理器配置问题、代理机制问题和异常处理问题等。为了确保事务的生效,应该正确配置事务管理器,并确保需要事务支持的方法具有正确的访问权限。
二、类内部访问
例:类内部非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 调用 B。
@Service
public class Demo {public void A() {this.B();}@Transactionalpublic void B() {......}
}
在该Service类中使用AopContext.currentProxy()获取代理对象
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableTransactionManagement
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}}
((ServiceA)AopContext.currentProxy()).doSave(user);
类内部访问导致事务失效的原因有以下几点:
1. 事务控制的范围不正确:事务的开始和结束应该在整个业务逻辑的最外层,如果在类内部进行了事务的开始和结束操作,可能会导致事务的范围不正确,从而导致事务失效。
2. 事务隔离级别不正确:事务的隔离级别决定了事务对并发操作的影响程度,如果在类内部进行了对事务隔离级别的设置,可能会导致事务的隔离级别不正确,从而导致事务失效。
3. 异常处理不正确:在类内部进行事务操作时,如果没有正确处理异常,可能会导致事务无法正确回滚,从而导致事务失效。
4. 数据库连接不正确:在类内部进行事务操作时,如果数据库连接不正确,可能会导致事务无法正确执行,从而导致事务失效。
5. 并发操作冲突:在类内部进行事务操作时,如果多个线程同时对同一数据进行操作,可能会导致并发操作冲突,从而导致事务失效。
总结起来,类内部访问导致事务失效的原因主要包括事务控制范围不正确、事务隔离级别不正确、异常处理不正确、数据库连接不正确以及并发操作冲突等。为了保证事务的正确执行,应该在整个业务逻辑的最外层进行事务的开始和结束操作,并正确处理异常、设置正确的隔离级别,同时注意数据库连接的正确性和并发操作的冲突。
三、数据库不支持事务
MyISAM和InnoDB是MySQL中两种常见的存储引擎。它们在很多方面有所不同,其中一个重要的区别是事务支持。
事务是一组数据库操作,要么全部成功执行,要么全部回滚。事务的支持对于确保数据的完整性和一致性非常重要。下面是一些关于为什么InnoDB支持事务而MyISAM不支持事务的详细介绍:
1. 锁级别:MyISAM使用表级锁定,这意味着当一个操作正在对表执行时,其他操作无法同时进行。这种锁定机制无法支持并发操作和事务的一致性。而InnoDB使用行级锁定,只锁定所需的行,这允许多个事务同时进行,并提高了并发性能。
2. 崩溃恢复:InnoDB支持崩溃恢复机制,这意味着在数据库发生故障或崩溃时,可以通过恢复日志来还原到之前的状态。而MyISAM没有这种机制,因此在发生故障时可能会导致数据丢失或损坏。
3. ACID特性:InnoDB支持ACID(原子性、一致性、隔离性和持久性)特性,这是事务处理的关键要素。原子性确保事务中的所有操作要么全部执行成功,要么全部回滚。一致性确保事务开始和结束时数据库的状态是一致的。隔离性确保并发事务之间的相互隔离,避免数据冲突。持久性确保一旦事务提交,其结果将永久保存在数据库中。而MyISAM不支持ACID特性,不能保证事务的一致性和持久性。
4. 外键约束:InnoDB支持外键约束,可以确保数据的引用完整性。外键可以定义在一个表中的列,引用另一个表中的主键列。这样可以保证数据的完整性,避免了数据的不一致。而MyISAM不支持外键约束。
综上所述,InnoDB支持事务是因为其使用了行级锁定、崩溃恢复机制、ACID特性和外键约束。这些特性使得InnoDB成为MySQL中更可靠和适合处理事务的存储引擎。
四、异常类型不匹配
@Transactional 注解默认只处理运行时异常( RuntimeException 和 error),而不会处理受检异常( Exception 的子类)。当抛出未被捕获的运行时异常时,Spring 会触发事务回滚操作,将之前的操作撤销;而对于未被捕获的受检异常,Spring 不会触发事务回滚操作。如果需要处理受检异常并触发事务回滚,可以通过 rollbackFor 和 noRollbackFor 属性来指定需要回滚或不需要回滚的异常类型。
/*** 非运行异常,且没有通过 rollbackFor 指定抛出的异常,不生效** @param id* @return* @throws Exception*/
@Transactional
public boolean testException(int id) throws Exception {//运行代码throw new Exception("参数异常");
}
这种情况需指定异常,如:
@Transactional(rollbackFor = Exception.class)Plain Text
五、传播属性设置问题
@Transactional默认的事务传播机制是:REQUIRED,若指定成了NOT_SUPPORTED、NEVER事务传播机制,则事物不生效,如:
@Transactional(propagation = Propagation.NOT_SUPPORTED)
六、捕获异常未抛出
例:
@Transactional
public void A(){try{......}catch(Exception e){// 未抛异常}
}
捕获异常未抛出导致事务失效的原因可能有以下几个:
1. 异常被捕获后没有进行处理或重新抛出:在事务处理过程中,如果出现异常,应该及时捕获并进行相应的处理,例如回滚事务或者进行异常处理逻辑。如果捕获异常后没有进行处理或重新抛出,事务就会终止,导致事务失效。
2. 异常被捕获后没有进行事务回滚:在事务处理过程中,如果出现异常,应该及时进行事务回滚,将数据库操作回滚到事务开始之前的状态。如果捕获异常后没有进行事务回滚,事务就会终止,导致事务失效。
3. 异常被捕获后没有进行事务提交:在事务处理过程中,如果出现异常,应该及时进行事务提交,将数据库操作提交到数据库中。如果捕获异常后没有进行事务提交,事务就会终止,导致事务失效。
4. 异常被捕获后没有进行日志记录或通知:在事务处理过程中,如果出现异常,应该及时进行日志记录或通知相关人员,以便及时处理异常情况。如果捕获异常后没有进行日志记录或通知,事务就可能失效,导致后续操作无法正常进行。
综上所述,捕获异常未抛出导致事务失效的原因主要是没有及时处理异常、没有进行事务回滚或提交、没有进行日志记录或通知等。为了保证事务的有效性,应该在捕获异常时及时进行相应的处理和操作。
七、Bean没有纳入Spring容器管理
例:
// 注释调@Component,该类没被Spring管理,事物也是不生效的// 注释调@Component,该类没被Spring管理,事物也是不生效的
public class Demo {@Transactional(rollbackFor = Exception.class)public void A() {......}
}
Bean没有纳入Spring容器管理导致事务失效的原因可能有以下几个:
1. 配置错误:可能是由于配置文件中没有正确配置事务管理器或者没有启用事务注解驱动。
2. 作用域错误:如果将Bean的作用域配置为prototype,每次获取该Bean时都会创建一个新的实例,这样事务管理器无法对其进行事务管理。
3. AOP代理问题:Spring的事务管理通过AOP代理实现,如果Bean没有被代理,那么事务管理器无法对其进行事务控制。这可能是因为Bean没有实现接口或者没有使用基于接口的代理。
4. Bean生命周期问题:如果Bean的生命周期不受Spring容器管理,那么事务管理器无法对其进行事务控制。这可能是因为Bean是通过其他方式创建的,而不是通过Spring容器创建的。
总之,如果Bean没有纳入Spring容器管理,那么事务管理器无法对其进行事务控制,从而导致事务失效。为了确保事务有效,需要确保Bean正确配置并纳入Spring容器管理。
八、事务方法内启动新线程进行异步操作
例:
@Transactional(rollbackFor= BizException.class)public int transfer2(String from,String to, int money){accountDao.decrMoney(from,money);new Thread(()->{int c = 5/0;accountDao.addMoney(to,money);}).start();return 1;}
事务方法内启动新线程进行异步操作导致事务失效的原因有以下几点:
1. 事务的隔离级别:如果事务的隔离级别是读未提交(Read Uncommitted),则新线程可能会读取到未提交的数据,导致数据不一致。
2. 事务的提交时机:如果新线程的异步操作在事务提交之前完成,那么事务中的操作将无法回滚,导致数据不一致。
3. 事务的传播行为:如果新线程的异步操作在事务方法内部,且事务传播行为为REQUIRED_NEW(新建事务),则新线程将会开启一个新的事务,与原事务无关,导致事务失效。
4. 异步操作的异常处理:如果新线程的异步操作发生异常,且没有进行合适的异常处理,那么事务中的操作将无法回滚,导致数据不一致。
综上所述,事务方法内启动新线程进行异步操作可能导致事务失效的原因主要是隔离级别、事务的提交时机、事务的传播行为以及异步操作的异常处理不当。为避免事务失效,应该在事务方法内部谨慎使用异步操作,并确保适当处理异步操作的异常,以及选择合适的事务隔离级别和传播行为。