Spring事务失效?这些坑你踩过吗?

程序员科技 2025-03-23 20:10:41

在使用 Spring 框架进行开发时,事务管理是保障数据一致性的关键部分。然而,不少开发者会遇到事务失效的情况,即便添加了@Transactional注解,事务也未能如预期生效,着实令人困扰。下面为大家详细剖析 Spring 事务失效的常见场景及应对方法。

事务未开启配置问题

在传统 Spring 项目中,若applicationContext.xml文件未正确配置事务相关参数,事务将无法生效。务必仔细检查配置文件,尤其是pointcut标签中的切入点匹配规则。确保execution表达式中的包路径、类名以及方法名通配符等设置正确,以精准匹配需要事务管理的方法所在的类和方法。例如:

<tx:advice id="advice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes></tx:advice><aop:config> <aop:pointcut expression="execution(* com.susan.*.*(..))" id="pointcut"/> <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/></aop:config>事务方法非 public

Spring 默认仅对public方法上的@Transactional注解生效。若将@Transactional注解置于非public方法上,需及时将方法修改为public。

异常捕获导致事务失效

自己吞了异常

若在代码中手动try...catch异常后未手动抛出,Spring 事务将不会回滚。正确做法是捕获异常后手动抛出RuntimeException或其子类,示例如下:

@Slf4j@Servicepublic UserService { @Transactional public void add(UserModel userModel) { try { saveData(userModel); updateData(userModel); } catch (Exception e) { log.error(e.getMessage(), e); throw new RuntimeException(e); } }}

手动抛了别的异常

Spring 事务默认仅回滚RuntimeException和Error,普通Exception不会触发回滚。因此,建议在事务方法中抛出RuntimeException,或者在@Transactional注解中指定rollbackFor = Exception.class,代码如下:

@Transactional(rollbackFor = Exception.class)public void add(UserModel userModel) throws Exception { saveData(userModel); updateData(userModel);}

自定义了回滚异常

使用@Transactional注解声明事务时,若设置rollbackFor参数来自定义回滚异常,务必确保参数设置准确。例如,期望自定义的BusinessException异常触发事务回滚,代码中抛出的异常必须是BusinessException或其子类,否则如抛出SqlException等其他类型异常,事务将不会回滚。一般建议将rollbackFor参数设置为Exception或Throwable,以确保大部分异常能触发事务回滚。

事务传播行为设置错误

使用@Transactional注解时,可通过设置propagation参数指定事务的传播特性。若设置错误,事务可能无法生效。例如,若希望方法在新事务中执行,应设置propagation = Propagation.REQUIRES_NEW,而非Propagation.NEVER。需依据业务逻辑需求,合理选择事务传播特性,其中只有REQUIRED、REQUIRES_NEW、NESTED这三种传播特性会创建新事务,示例如下:

@Servicepublic UserService { @Transactional(propagation = Propagation.REQUIRES_NEW) public void add(UserModel userModel) { saveData(userModel); updateData(userModel); }}事务隔离级别设置不当

不合适的事务隔离级别可能引发数据一致性问题。开发者需根据业务需求选择恰当的事务隔离级别。若业务场景对数据一致性要求较高,为避免脏读、不可重复读和幻读,不宜设置READ_UNCOMMITTED这种较低的事务隔离级别。通常,READ_COMMITTED能满足多数业务场景,设置方式如下:

@Servicepublic UserService { @Transactional(isolation = Isolation.READ_COMMITTED) public void updateUser(User user) { // 更新用户信息 }}事务超时设置不合理

事务超时设置不合理会致使事务在执行过程中自动回滚。开发者应根据业务逻辑的复杂程度和预计执行时间,合理设置事务超时时间。若业务逻辑通常在几秒内完成,不宜将超时时间设置过短,如 1 秒就不太合适,可设置为 60 秒或更长,具体依实际情况而定,示例代码如下:

@Servicepublic UserService { @Transactional(timeout = 60) // 超时时间设置为60秒 public void updateUser(User user) { // 更新用户信息 }}只读事务设置错误

若事务被标记为只读(readOnly = true),则不能进行任何修改操作,否则会抛出异常。当需要进行修改操作时,记得将readOnly设置为false,示例如下:

@Servicepublic UserService { @Transactional(readOnly = false) public User getUserById(Long id) { // 查询用户信息,若有修改操作,需设置readOnly为false }}数据库连接问题

数据库连接池配置不当或数据库连接断开,会导致事务无法正常提交或回滚。在配置数据源时,要合理设置连接池参数,如最大连接数。若最大连接数设置过小,可能导致事务失败。可根据项目并发量等情况,合理设置最大连接数,例如:

@Configurationpublic AppConfig { @Bean public DataSource dataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/test"); config.setUsername("root"); config.setPassword("password"); config.setMaximumPoolSize(10); // 设置最大连接数 return new HikariDataSource(config); }}事务管理器不匹配

使用错误的事务管理器也会引发事务问题。例如,在使用 JPA 时配置了 JDBC 事务管理器,或在使用 MyBatis 时配置了 Hibernate 事务管理器。开发者要依据项目使用的持久化框架,选择正确的事务管理器。如使用 JPA 时,配置JpaTransactionManager:

@Configurationpublic AppConfig { @Bean public PlatformTransactionManager transactionManager(EntityManagerFactory emf) { return new JpaTransactionManager(emf); }}

若使用基于数据源的事务管理,则配置DataSourceTransactionManager:

@Configurationpublic AppConfig { @Bean public PlatformTransactionManager transactionManager(DataSource dataSource) { return new DataSourceTransactionManager(dataSource); }}总结

Spring 事务失效的问题只要在开发过程中多加留意、仔细排查,是能够有效避免的。希望本文分享的解决方案能为广大开发者提供帮助。若在实际项目中遇到其他关于 Spring 事务的问题,或有不同的事务失效场景,欢迎在评论区留言交流,共同提升项目事务管理的稳定性与可靠性。

0 阅读:0

程序员科技

简介:感谢大家的关注