Springboot+Mybatis-解决JPA、JDBC冲突导致@Transactional事务不生效不回滚的问题
环境
- Springboot 1.5.6
- Mybatis 3.5.3
- Mybatis-plus 3.3.1
问题产生原因
通过打印事务管理器,发现Mybatis环境下的事务管理器实例为JpaTransactionManager对象,明显不对。
Mybatis事务中使用Jpa的事务管理导致事务失效回滚失败问题;
ORM事务管理器对应关系:
- Mybatis -> org.springframework.jdbc.datasource.DataSourceTransactionManager@50061d56
- JPA -> org.springframework.orm.jpa.JpaTransactionManager@204d101
解决方法
添加个@Bean配置,注册jdbc事务管理器DataSourceTransactionManager,其中dataSource由框架自动注入;
项目中jpa、mybatis混用时,需要在使用@Bean(“xxx”)注解注册对象时,对dataSource、sqlSessionFactory、DataSourceTransactionManager显示配置;
//注册
@Configuration
public class MybatisConfig {
@Bean("jdbcTransactionManager")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
//使用
@Transactional(value = "jdbcTransactionManager",rollbackFor = {Exception.class})
public void TestTran4Salf(){
...
}
排查过程
输出SQL执行过程
三种配置方式:
- yml配置
配置对象:org.apache.ibatis.session.Configuration
mybatis:
configuration:
#事务注册日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus:
mapper-locations: classpath:/mapper/**/**.xml
configuration:
#事务注册日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
logging:
level:
root: info
#事务提交回滚日志
org.springframework.jdbc.datasource.DataSourceTransactionManager: debug
- 修改SqlSessionFactory属性
@Configuration
public class MybatisConfig {
@Autowired
private List<SqlSessionFactory> sqlSessionFactoryList;
@PostConstruct
public void addSqlInterceptor() {
for (SqlSessionFactory sqlSessionFactory : sqlSessionFactoryList) {
sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
sqlSessionFactory.getConfiguration().setCallSettersOnNulls(true);//返回查询中的null值
sqlSessionFactory.getConfiguration().setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);//输出执行SQL
}
}
}
- 自定义ConfigurationCustomizer对象
@Configuration
public class MybatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());
configuration.setCallSettersOnNulls(true);//返回查询中的null值
configuration.setMapUnderscoreToCamelCase(false);//表字段使用下划线命名
configuration.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);//输出执行SQL
};
}
}
是否开启了事务支持
注解@EnableTransactionManagement标记启用事务,没加事务不会起作用,手动调用session对象的rollback()也无效;
@EnableTransactionManagement
@SpringBootApplication()
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}
平台管理器是否正确
注入一个事务管理器实例,打印出来看看实例对象是否匹配当前ORM;
@Autowired
PlatformTransactionManager platformTransactionManager;
@Test
public String testTran() {
System.out.print(">>>>>事务管理器:"+platformTransactionManager);
....
}
将执行的SQL打印到控制台方便查看事务执行过程
打开Mybatis执行SQL日志,方便滚出事务执行情况;
>>>>>事务管理器:org.springframework.jdbc.datasource.DataSourceTransactionManager@2fbbdcaCreating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
JDBC Connection [ProxyConnection[PooledConnection[org.postgresql.jdbc.PgConnection@14fc5be1]]] will be managed by Spring
==> Executing: select * from mytable;
<== Total: 2
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] from current transaction --事务
==> Preparing: insert into mytable(log_id, create_date) VALUES ('1678872584952_1',now());
==> Parameters:
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] from current transaction --事务
==> Preparing: insert into mytable(log_id, create_date) VALUES ('1678872584952_2',now());
==> Parameters:
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] from current transaction --事务
==> Preparing: insert into mytable(log_id, create_date) VALUES ('1678872584952_2',now());
==> Parameters:
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1149ccc6] --事务
引起事务失效的几个坑
坑1:未启用Spring事务托管
启动类上需要显示加入@EnableTransactionManagement注解,否则事务不生效。
坑2:ORM混用导致平台事务管理器有多个
Mybatis和JPA事务管理器对应关系:
- Mybatis -> org.springframework.jdbc.datasource.DataSourceTransactionManager@50061d56
- JPA -> org.springframework.orm.jpa.JpaTransactionManager@204d101
坑3:默认业务异常不回滚
@Transactional() 未指定时,只对运行时异常回滚;
@Transactional(rollbackFor = {Exception.class})此时会对所有异常回滚;
坑4:手动捕获了异常没有抛出也会引起事务失效
try {
mapper.insert(sql1);
mapper.insert(sql2);
}catch (Exception e){
e.printStackTrace();
throw e; //注释掉这行将不回滚
}