Springboot+Mybatis-解决JPA、JDBC冲突导致@Transactional事务不生效不回滚的问题

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;  //注释掉这行将不回滚
}

坑5:@Transaction注解只对方法名为pubic的才生效,其他事物不会生效。

### 关于 UniApp 框架的推荐资源与教程 #### 1. **Uniapp 官方文档** 官方文档是最权威的学习资料之一,涵盖了从基础概念到高级特性的全方位讲解。对于初学者来说,这是了解 UniApp 架构和技术细节的最佳起点[^3]。 #### 2. **《Uniapp 从入门到精通:案例分析与最佳实践》** 该文章提供了系统的知识体系,帮助开发者掌握 Uniapp 的基础知识、实际应用以及开发过程中的最佳实践方法。它仅适合新手快速上手,也能够为有经验的开发者提供深入的技术指导[^1]。 #### 3. **ThorUI-uniapp 开源项目教程** 这是一个专注于 UI 组件库设计和实现的教学材料,基于 ThorUI 提供了一系列实用的功能模块。通过学习此开源项目的具体实现方式,可以更好地理解如何高效构建美观且一致的应用界面[^2]。 #### 4. **跨平台开发利器:UniApp 全面解析与实践指南** 这篇文章按照章节形式详细阐述了 UniApp 的各个方面,包括但限于其工作原理、技术栈介绍、开发环境配置等内容,并附带丰富的实例演示来辅助说明理论知识点。 以下是几个重要的主题摘选: - **核心特性解析**:解释了跨端运行机制、底层架构组成及其主要功能特点。 - **开发实践指南**:给出了具体的页面编写样例代码,展示了同设备间 API 调用的方法论。 - **性能优化建议**:针对启动时间缩短、图形绘制效率提升等方面提出了可行策略。 ```javascript // 示例代码片段展示条件编译语法 export default { methods: { showPlatform() { console.log(process.env.UNI_PLATFORM); // 输出当前平台名称 #ifdef APP-PLUS console.log('Running on App'); #endif #ifdef H5 console.log('Running on Web'); #endif } } } ``` #### 5. **其他补充资源** 除了上述提到的内容外,还有许多在线课程视频可供选择,比如 Bilibili 上的一些免费系列讲座;另外 GitHub 和 GitCode 平台上也有少优质的社区贡献作品值得借鉴研究。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值