@Transactional注解异常报错之多数据源详解

yizhihongxing

当使用 @Transactional 注解时,可能会遇到多数据源的异常问题。本篇攻略将会详细讲解这个问题的根本原因并且提供两个示例来说明。

1. 什么是多数据源

多数据源即指一个系统维护了多个数据库,每个数据库可能拥有不同的表或者对象。在应用程序中,连接各个数据库的连接信息通常是不同的。

2. 问题描述

当使用 @Transactional 注解时,会抛出异常,异常信息如下所示:

org.springframework.transaction.TransactionSystemException: Could not commit JDBC transaction; nested exception is java.sql.SQLException: Unable to commit against JDBC Connection
...
Caused by: java.sql.SQLException: Unable to commit against JDBC Connection
...

3. 解决方法

在多数据源的环境中,由于存在多个连接,因此,从一个连接(如主数据源)中启动的事务可能无法在其他连接(如从数据源)中进行提交,从而导致错误。要解决这个问题,可以使用两种方法:

3.1 在 @Transactional 注解中指定事务管理器

使用 @Transactional 注解的时候,可以通过指定数据源的方式来实现,代码示例如下:

@Service
public class UserService {
    @Autowired
    @Qualifier("orderTransactionManager")
    private PlatformTransactionManager orderTransactionManager;
    @Autowired
    @Qualifier("memberTransactionManager")
    private PlatformTransactionManager memberTransactionManager;

    @Transactional(transactionManager = "orderTransactionManager")
    public void doSomething1() {
        //do something in order database
    }

    @Transactional(transactionManager = "memberTransactionManager")
    public void doSomething2() {
        //do something in member database
    }
}

3.2 使用 ChainedTransactionManager 转发事务

使用 ChainedTransactionManager 来转发事务是另一种常见的方法。它是一个特殊的 PlatformTransactionManager,它将多个 PlatformTransactionManager 组合成一个事务管理器。

ChainedTransactionManager 与其他 Spring 的事务管理器一样,它可以像其他 TransactionManager 一样进行调用,只是需要同时提交和回滚各个 TransactionManager。

下面是示例代码:

@Configuration
@Table
@EnableTransactionManagement
@SpringBootApplication
public class SpringMultiDatasourceDemoApplication {
    @Autowired
    private DataSource orderDataSource;
    @Autowired
    private DataSource memberDataSource;

    @Bean(name = "orderTransactionManager")
    public PlatformTransactionManager orderTransactionManager() {
        return new DataSourceTransactionManager(orderDataSource);
    }

    @Bean(name = "memberTransactionManager")
    public PlatformTransactionManager memberTransactionManager() {
        return new DataSourceTransactionManager(memberDataSource);
    }

    @Bean
    public PlatformTransactionManager txManager() {
        List<PlatformTransactionManager> tms = new ArrayList<>(2);
        tms.add(orderTransactionManager());
        tms.add(memberTransactionManager());
        return new ChainedTransactionManager(tms);
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringMultiDatasourceDemoApplication.class, args);
    }
}

4. 示例说明

4.1 第一个示例

第一个示例中,我们有两个数据源:order 和 member,它们都具有一个名为 User 的表,我们要使用 @Transactional 注解执行事务,对两个表进行操作。

代码示例:

@Service
public class UserService {
    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;

    @Autowired
    public UserService(OrderRepository orderRepository, MemberRepository memberRepository) {
        this.orderRepository = orderRepository;
        this.memberRepository = memberRepository;
    }

    @Transactional
    public void save(User user) {
        orderRepository.save(user);
        memberRepository.save(user);
    }
}

当使用上面的代码时,会因为多数据源而出现异常,导致事务无法提交。我们可以使用方法 1 中的方式来指定数据源,代码示例如下:

@Service
public class UserService {
    @Autowired
    @Qualifier("orderTransactionManager")
    private PlatformTransactionManager orderTransactionManager;
    @Autowired
    @Qualifier("memberTransactionManager")
    private PlatformTransactionManager memberTransactionManager;
    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;

    @Autowired
    public UserService(OrderRepository orderRepository, MemberRepository memberRepository) {
        this.orderRepository = orderRepository;
        this.memberRepository = memberRepository;
    }

    @Transactional(transactionManager = "orderTransactionManager")
    public void save(User user) {
        orderRepository.save(user);
        memberRepository.save(user);
    }
}

4.2 第二个示例

第二个示例中,我们有两个数据源:order 和 member,它们都具有一个名为 User 的表,我们要使用 @Transactional 注解执行事务,对两个表进行操作。

代码示例:

@Service
public class UserService {
    private final OrderRepository orderRepository;
    private final MemberRepository memberRepository;

    @Autowired
    public UserService(OrderRepository orderRepository, MemberRepository memberRepository) {
        this.orderRepository = orderRepository;
        this.memberRepository = memberRepository;
    }

    @Transactional
    public void save(User user) {
        orderRepository.save(user);
        memberRepository.save(user);
    }
}

当使用上面的代码时,会因为多数据源而出现异常,导致事务无法提交。我们可以使用方法 2 中的方式来转发事务,代码示例如下:

@Configuration
@Table
@EnableTransactionManagement
@SpringBootApplication
public class SpringMultiDatasourceDemoApplication {
    @Autowired
    private DataSource orderDataSource;
    @Autowired
    private DataSource memberDataSource;

    @Bean(name = "orderTransactionManager")
    public PlatformTransactionManager orderTransactionManager() {
        return new DataSourceTransactionManager(orderDataSource);
    }

    @Bean(name = "memberTransactionManager")
    public PlatformTransactionManager memberTransactionManager() {
        return new DataSourceTransactionManager(memberDataSource);
    }

    @Bean
    public PlatformTransactionManager txManager() {
        List<PlatformTransactionManager> tms = new ArrayList<>(2);
        tms.add(orderTransactionManager());
        tms.add(memberTransactionManager());
        return new ChainedTransactionManager(tms);
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringMultiDatasourceDemoApplication.class, args);
    }
}

5. 总结

本篇攻略详细介绍了解决多数据源下 @Transactional 注解异常的两种方法,分别为指定事务管理器和使用 ChainedTransactionManager 转发事务。通过两个示例,我们可以发现,无论哪种方法,都可以解决在多数据源环境下的事务异常问题。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:@Transactional注解异常报错之多数据源详解 - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • Linux发展历史大事编年表(截止2013年)

    Linux发展历史大事编年表(截止2013年) 以下是Linux发展历史中的重要事件编年表。本文包含了从最早的Linux内核版本到2013年的重要事件。 1991年 Linus Torvalds创造了最初的 Linux 0.01 内核并公开发布,并于随后的几年中继续更新内核。 1992年 GNU General Public License (GPL) 2发…

    database 2023年5月22日
    00
  • JPA如何将查询结果转换为DTO对象

    JPA(Java Persistence API)是Java EE中被称为“java ORM 映射标准”的框架,它提供了一组API,用于在Java应用程序中管理关系数据的持久化。在使用JPA进行数据查询的过程中,我们常常需要将查询结果转换为DTO对象,以便在应用程序中更好地管理和处理查询结果。 下面是将查询结果转换为DTO对象的完整攻略: 1. 创建DTO对…

    database 2023年5月22日
    00
  • mybatis单笔批量保存实体数据的方法

    一、概述 mybatis 是一个优秀的 ORM 框架,提供了单笔保存实体数据的方法,也支持批量保存实体数据的方法。批量保存实体数据要比单笔保存实体数据的效率高,因为避免了频繁连接数据库以及频繁提交 SQL 的开销。本文将详细讲解 mybatis 单笔批量保存实体数据的方法。 二、单笔保存实体数据 单笔保存实体数据的方法比较简单,代码如下: Integer s…

    database 2023年5月21日
    00
  • Mysql中使用时间查询的详细图文教程

    MySQL中使用时间查询是一项非常常见的操作,它可以用于查询某一时间段内的数据。以下是在MySQL数据库中使用时间查询的详细攻略: 1. 创建测试表格 在开始时间查询之前,先创建一个测试表格。可以使用以下的SQL命令创建一个名为test_table的测试表格: CREATE TABLE `test_table` ( `id` int(11) NOT NULL…

    database 2023年5月22日
    00
  • 详解MySQL系统变量的查看和修改

    MySQL系统变量是MySQL服务器的某些配置参数,可以通过查看和修改这些变量来调整服务器的行为或优化性能。本文将详细介绍MySQL系统变量的查看和修改方式,并结合实例说明。 查看MySQL系统变量 通过SHOW VARIABLES命令查看MySQL系统变量 在MySQL命令行客户端中,使用“SHOW VARIABLES”命令可以列出当前MySQL服务器的所…

    MySQL 2023年3月9日
    00
  • Redis 向集群添加新节点

      1. 启动集群服务(向集群添加新节点,则说明,集群是已知的)。   2. 搭建将要添加到集群的节点     (1)以集群的方式对新添加的节点进行配置:redis.conf.     (2)启动节点实例服务.    3.集群管理 可参考  Redis cluster tutorial 官网指导文档,往往是最新的。 一、启动集群服务:【命令在集群服务器执行】…

    Redis 2023年4月11日
    00
  • MySQL命令show full processlist

    命令格式: SHOW [FULL] PROCESSLIST SHOW PROCESSLIST显示哪些线程正在运行,如果您不使用FULL关键词,则只显示每个查询的前100个字符 各列的含义和用途: id: 一个标识 user: 显示当前用户,如果不是root,这 个命令就只显示你权限范围内的sql语句。 host: 显示这个语句是从哪个ip的哪个端口上发出的 …

    MySQL 2023年4月13日
    00
  • WIN7下ORACLE10g服务端和客户端的安装图文教程

    以下是在WIN7下安装ORACLE10g服务端和客户端的完整攻略: 1. 下载安装文件 首先,需要从ORACLE官网下载安装文件,这里建议下载10.2.0.1版本,因为它相对比较稳定。下载链接为:https://www.oracle.com/database/technologies/10201winsoft.html 2. 安装服务端 2.1 解压安装文件…

    database 2023年5月21日
    00
合作推广
合作推广
分享本页
返回顶部