使用transactional不起作用简单分析

首先要考虑所使用的数据库是不是支持事务性的,针对mysql必须使用innodb引擎来支持事务。
 SHOW ENGINES;查看当前引擎状态。
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.00 sec)
其次,要注意transactional要捕获的是运行时异常RuntimeException。
在业务代码中,有如下两种情况,比如:
throw new RuntimeException("xxxxxxxxxxxx"); 事务回滚
throw new Exception("xxxxxxxxxxxx"); 事务没有回滚
1). spring的AOP即声明式事务管理默认是针对unchecked exception回滚。也就是默认对RuntimeException()异常或是其子类进行事务回滚;checked异常,即Exception可try{}捕获的不会回滚,如果使用try-catch捕获抛出的unchecked异常后没有在catch块中采用页面硬编码的方式使用spring api对事务做显式的回滚,则事务不会回滚, “将异常捕获,并且在catch块中不对事务做显式提交=生吞掉异常” ,要想捕获非运行时异常则需要如下配置:
解决办法:
1.在针对事务的类中抛出RuntimeException异常,而不是抛出Exception。
2.在txAdive中增加rollback-for,里面写自己的exception,例如自己写的exception:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
     <tx:method name="*" rollback-for="com.cn.untils.exception.XyzException"/>
   </tx:attributes>
 </tx:advice>
3.针对我们自己抛出的异常,可以通过rollbackFor属性来让事务去识别。
例如:
@Transactional(rollbackFor={RuntimeException.class, CommonException.class})
public void test3(User user1) throws CommonException {}

这些都没有问题,就要开启日志,把启动日志打印出来,查看一下是不是我们定义的事务管理器扫描是否成功。有没有进行声明:
<tx:annotation-driven transaction-manager="transactionManager" />放置位置对不对,通常我们放在数据源配置完成,事务管理器定义完成之后声明,一定要注意,spring service bean的扫描早于这个声明之前。也有可能声明放在controller,而去service层加注解显然是不行的。
其次,如果我们定义多个数据源,而针对每个数据源也都定义相应的事务管理器,这个时候如果我们相用指定的事务管理器去回滚我们指定的业务模块。即通过value属性即可来制定唯一的事务管理器:
TransactionManager,那么对于只需要一个事务管理的方法,问题就简单多了:
@Transactional(value = "TransactionManager1") public void test(String a) { // business operation }
这里稍微提一下:
针对多数据库源可以使用 Spring + Atomikos 分布式事务实现方式来实现声明式事务,也可以切面编程来实现编程式事务。



### 关于 MyBatis 中 Non-Transactional SqlSession 的问题 在 Spring Boot 配置了 MyBatis 和 Druid 数据库连接池的情况下,如果发现每次 SQL 执行都会打印 `Creating a new SqlSession` 和 `Closing non transactional SqlSession` 日志,则可能表明当前的操作未被纳入事务管理范围。这可能导致某些更新操作无法正常生效。 #### 原因分析 1. **Non-Transactionality of SqlSession**: 当 MyBatis 创建了一个非事务性的 SqlSession (`non-transactional`) 并执行更新操作时,如果没有显式的事务控制机制,该会话会在完成操作后立即关闭并释放资源[^1]。这种行为可能会导致部分更新未能成功提交到数据库中。 2. **GTID Consistency Violation**: 如果使用的 MySQL 数据库启用了 GTID (Global Transaction Identifier),则对于非事务性表的更新操作有严格的要求——它们只能在一个单独的自动提交语句或者单条语句事务中进行[^3]。这意味着任何涉及多步逻辑的更新操作都必须由事务包裹起来才能满足一致性约束。 3. **Spring 与 MyBatis 的集成不足**: 在 MyBatis-Spring 整合过程中,默认情况下不会自动启用事务支持除非特别配置好相应的声明式事务管理器以及扫描路径设置[^2]。因此即使调用了 insert/update/delete 方法也可能因为缺乏必要的上下文环境而失败。 #### 解决方案 以下是几种常见的解决办法: ##### 方案一:使用 @Transactional 注解 通过为服务层的方法添加 `@Transactional` 注解可以确保整个方法运行期间都有一个持久化的 SqlSession 实例存在直到最后才统一提交更改。同时需要确认项目中的 spring-context.xml 文件包含了如下配置项以便开启基于注解方式定义的事物处理功能: ```xml <tx:annotation-driven transaction-manager="transactionManager"/> ``` 另外还需指定具体的 PlatformTransactionManager 类型比如 DataSourceTransactionManager 来关联实际的数据源对象实例[^4]: ```java @Bean(name = "transactionManager") public DataSourceTransactionManager dataSourceTransactionManager(@Qualifier("dataSource") DataSource dataSource){ return new DataSourceTransactionManager(dataSource); } ``` ##### 方案二:手动管理 SqlSession 生命周期 如果不希望依赖框架自带的功能也可以考虑自己接管 SqlSession 的生命周期,在业务代码内部显式地打开一个新的 session 对象然后在其作用域结束之前主动 commit 或 rollback 变更内容后再销毁它。不过这种方式通常只适用于非常特殊的需求场景下并不推荐广泛采用因为它违背了 IOC/DI 设计原则增加了耦合度同时也容易引发潜在错误风险。 下面是一个简单的例子展示如何实现这一点: ```java @Autowired private SqlSessionFactory sqlSessionFactory; public void performUpdate(){ try(SqlSession sqlSession = this.sqlSessionFactory.openSession()){ Mapper mapper = sqlSession.getMapper(Mapper.class); // Execute your updates here... mapper.updateSomething(); sqlSession.commit(); }catch(Exception e){ log.error(e.getMessage(),e); } } ``` 注意以上片段仅作为演示用途具体实现细节应根据实际情况调整优化。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值