springboot mybaits 连接多数据源

-----------------------------------------------------------------------------------------------------------

在 Spring Boot 中使用 MyBatis 连接多数据源,通常可以通过配置多个数据源实例并绑定不同的 Mapper 路径来实现。以下是详细步骤:

1. 准备依赖

在 pom.xml 中添加必要依赖(以 MySQL 为例):
xml
 
 
<!-- Spring Boot 核心 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- MyBatis 整合 Spring Boot -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.3.0</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

<!-- 数据源配置(可选,Spring Boot 2.0+ 自带 HikariCP) -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>
 

2. 配置多数据源信息

在 application.yml 中配置多个数据源的连接信息(以两个数据源为例):
yaml
 
 
spring:
  datasource:
    # 主数据源(primary)
    primary:
      jdbc-url: jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
    # 第二个数据源(secondary)
    secondary:
      jdbc-url: jdbc:mysql://localhost:3306/db2?useSSL=false&serverTimezone=UTC
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver

# MyBatis 配置(可选,如 mapper 位置、别名等)
mybatis:
  type-aliases-package: com.example.entity  # 实体类包路径
  configuration:
    map-underscore-to-camel-case: true  # 下划线转驼峰
 

3. 配置数据源实例

创建配置类,分别实例化两个数据源,并指定主数据源(@Primary)。

主数据源配置(PrimaryDataSourceConfig)

java
 
运行
 
 
 
 
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
// 扫描主数据源对应的 Mapper 接口,并指定 SqlSessionFactory
@MapperScan(
    basePackages = "com.example.mapper.primary",  // 主数据源的 Mapper 路径
    sqlSessionFactoryRef = "primarySqlSessionFactory"
)
public class PrimaryDataSourceConfig {

    // 实例化主数据源
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")  // 绑定配置前缀
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 主数据源的 SqlSessionFactory
    @Primary
    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactoryBean primarySqlSessionFactory(
            @Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 指定主数据源的 Mapper XML 路径(如果有)
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver()
                .getResources("classpath:mybatis/primary/*.xml")  // 例如:resources/mybatis/primary/
        );
        return sessionFactory;
    }
}
 

第二个数据源配置(SecondaryDataSourceConfig)

java
 
运行
 
 
 
 
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

@Configuration
// 扫描第二个数据源对应的 Mapper 接口
@MapperScan(
    basePackages = "com.example.mapper.secondary",  // 第二个数据源的 Mapper 路径
    sqlSessionFactoryRef = "secondarySqlSessionFactory"
)
public class SecondaryDataSourceConfig {

    // 实例化第二个数据源
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 第二个数据源的 SqlSessionFactory
    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactoryBean secondarySqlSessionFactory(
            @Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 指定第二个数据源的 Mapper XML 路径
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver()
                .getResources("classpath:mybatis/secondary/*.xml")  // 例如:resources/mybatis/secondary/
        );
        return sessionFactory;
    }
}
 

4. 编写 Mapper 和实体类

  • 主数据源的 Mapper 接口放在 com.example.mapper.primary 包下,对应的 XML 文件放在 resources/mybatis/primary/ 下。
  • 第二个数据源的 Mapper 接口放在 com.example.mapper.secondary 包下,对应的 XML 文件放在 resources/mybatis/secondary/ 下。
示例(主数据源 Mapper):
java
 
运行
 
 
 
 
// com.example.mapper.primary.UserMapper.java
public interface UserMapper {
    User getById(Long id);
}
 
对应的 XML:
xml
 
 
<!-- resources/mybatis/primary/UserMapper.xml -->
<mapper namespace="com.example.mapper.primary.UserMapper">
    <select id="getById" resultType="com.example.entity.User">
        select * from user where id = #{id}
    </select>
</mapper>
 

5. 使用数据源

在 Service 中直接注入对应的 Mapper 即可自动使用绑定的数据源:
java
 
运行
 
 
 
 
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;  // 自动关联主数据源

    @Autowired
    private OrderMapper orderMapper;  // 自动关联第二个数据源(假设 OrderMapper 在 secondary 包下)

    public User getUser(Long id) {
        return userMapper.getById(id);  // 查询 db1
    }

    public Order getOrder(Long id) {
        return orderMapper.getById(id);  // 查询 db2
    }
}
 

关键说明

  1. @Primary 注解:必须指定一个主数据源,否则 Spring 会因无法确定默认数据源而报错。
  2. Mapper 路径隔离:通过 @MapperScan 的 basePackages 区分不同数据源的 Mapper,避免冲突。
  3. 事务管理:如果需要事务,需为每个数据源配置 DataSourceTransactionManager,并在 Service 中通过 @Transactional(value = "事务管理器名称") 指定。
例如,为主数据源添加事务管理器:
java
 
运行
 
 
 
 
@Primary
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(
        @Qualifier("primaryDataSource") DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);
}
 
使用时:
java
 
运行
 
 
 
 
@Transactional(value = "primaryTransactionManager")
public void saveUser(User user) {
    userMapper.insert(user);
}
 
通过以上步骤,即可实现 Spring Boot + MyBatis 多数据源的配置和使用。如果需要更多数据源,按相同逻辑添加配置类即可。

-----------------------------------------------------------------------------------------------------------

Spring Boot 中使用 MyBatis 连接多数据源 事务控制
在 Spring Boot + MyBatis 多数据源场景中,事务控制需要为每个数据源单独配置事务管理器,并通过 @Transactional 注解指定使用哪个事务管理器。以下是详细实现步骤:

1. 为每个数据源配置事务管理器

多数据源环境下,每个数据源需要对应一个独立的事务管理器(DataSourceTransactionManager)。在之前的数据源配置类中添加事务管理器的定义:

主数据源配置类(补充事务管理器)

java
 
运行
 
 
 
 
@Configuration
@MapperScan(
    basePackages = "com.example.mapper.primary",
    sqlSessionFactoryRef = "primarySqlSessionFactory"
)
public class PrimaryDataSourceConfig {

    // 主数据源(省略,同上)
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 主数据源 SqlSessionFactory(省略,同上)
    @Primary
    @Bean(name = "primarySqlSessionFactory")
    public SqlSessionFactoryBean primarySqlSessionFactory(
            @Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
        // ... 同上
    }

    // 主数据源事务管理器(新增)
    @Primary  // 主事务管理器也需要加 @Primary
    @Bean(name = "primaryTransactionManager")
    public DataSourceTransactionManager primaryTransactionManager(
            @Qualifier("primaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
 

第二个数据源配置类(补充事务管理器)

java
 
运行
 
 
 
 
@Configuration
@MapperScan(
    basePackages = "com.example.mapper.secondary",
    sqlSessionFactoryRef = "secondarySqlSessionFactory"
)
public class SecondaryDataSourceConfig {

    // 第二个数据源(省略,同上)
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }

    // 第二个数据源 SqlSessionFactory(省略,同上)
    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactoryBean secondarySqlSessionFactory(
            @Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
        // ... 同上
    }

    // 第二个数据源事务管理器(新增)
    @Bean(name = "secondaryTransactionManager")
    public DataSourceTransactionManager secondaryTransactionManager(
            @Qualifier("secondaryDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
 

2. 在 Service 中指定事务管理器

通过 @Transactional 注解的 value 属性(或 transactionManager 属性,两者等价)指定使用哪个事务管理器。

示例:单数据源事务

java
 
运行
 
 
 
 
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;  // 主数据源的 Mapper

    @Autowired
    private OrderMapper orderMapper;  // 第二个数据源的 Mapper

    // 使用主数据源的事务管理器
    @Transactional(value = "primaryTransactionManager", rollbackFor = Exception.class)
    public void saveUser(User user) {
        userMapper.insert(user);
        // 如果发生异常,会回滚主数据源的操作
        if (user.getId() == null) {
            throw new RuntimeException("保存用户失败");
        }
    }

    // 使用第二个数据源的事务管理器
    @Transactional(value = "secondaryTransactionManager", rollbackFor = Exception.class)
    public void saveOrder(Order order) {
        orderMapper.insert(order);
        // 如果发生异常,会回滚第二个数据源的操作
    }
}
 

3. 跨数据源事务(分布式事务)

如果需要在一个方法中操作多个数据源,且要求 “要么都成功,要么都失败”,单靠本地事务管理器无法实现(本地事务只能管理单个数据源),此时需要分布式事务方案。
常用的分布式事务实现方式:
  • Seata:阿里开源的分布式事务框架,支持 AT、TCC 等模式,集成简单。
  • Hmily:基于 TCC 模式的分布式事务框架。
  • 2PC:传统分布式事务协议(性能较差,不推荐)。

以 Seata 为例的简单集成步骤:

  1. 添加 Seata 依赖:
    xml
     
     
    <dependency>
        <groupId>io.seata</groupId>
        <artifactId>seata-spring-boot-starter</artifactId>
        <version>1.6.1</version>
    </dependency>
    
     
     
  2. 配置 Seata(application.yml):
    yaml
     
     
    seata:
      tx-service-group: my_test_tx_group  # 事务组名称(需与 Seata 服务器配置一致)
      registry:
        type: nacos  # 注册中心类型(如 nacos、eureka 等)
        nacos:
          server-addr: 127.0.0.1:8848
      config:
        type: nacos
        nacos:
          server-addr: 127.0.0.1:8848
    
     
     
  3. 使用分布式事务注解:在跨数据源的方法上添加 @GlobalTransactional(Seata 提供的分布式事务注解),替代 @Transactional
    java
     
    运行
     
     
     
     
    @Service
    public class OrderService {
    
        @Autowired
        private UserMapper userMapper;  // 主数据源
    
        @Autowired
        private OrderMapper orderMapper;  // 第二个数据源
    
        // 分布式事务:同时操作两个数据源
        @GlobalTransactional(rollbackFor = Exception.class)
        public void createOrderAndUpdateUser(Order order, Long userId) {
            // 操作主数据源
            userMapper.updateBalance(userId, order.getAmount());
            // 操作第二个数据源
            orderMapper.insert(order);
            // 如果发生异常,Seata 会协调两个数据源回滚
            if (order.getId() == null) {
                throw new RuntimeException("创建订单失败");
            }
        }
    }
    
     
     

关键注意事项

  1. 本地事务限制:@Transactional 只能控制单个数据源的事务,跨数据源操作时需用分布式事务框架。
  2. 事务管理器名称:@Transactional(value = "xxxTransactionManager") 中的名称必须与配置类中 @Bean(name = "xxxTransactionManager") 一致。
  3. rollbackFor 属性:默认情况下,事务只对 RuntimeException 回滚,建议显式指定 rollbackFor = Exception.class 确保所有异常都回滚。
  4. 分布式事务成本:分布式事务会降低性能并增加复杂度,非必要场景建议避免跨数据源事务(例如通过业务设计将相关操作放在同一数据源)。

-----------------------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------------------

posted @ 2025-11-10 11:09  hanease  阅读(33)  评论(0)    收藏  举报