@DS多数据源baomidou的用法,事务处理

博客介绍了多数据源的使用场景,如数据库读写分离、项目业务有不同数据库。还给出了Spring Boot多数据源的配置,包括pom.xml和yml配置,提醒注意版本号及避免配置冲突。同时介绍了测试实现类,指出单个service方法不能跨数据源,需通过另外的bean调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多数据源的使用场景:

1. 为了系统性能,数据库采用了读写分离,应用写主库,读从库

2. 项目业务有不同的数据库,比如电商系统,有用户库、商品库、交易库。

pom.xml配置:(注意版本号,低于这个版本可能会启动报错。。。被坑过)

我的springBoot版本是:2.3.0.RELEASE

<dependency>
     <groupId>com.baomidou</groupId>
     <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
     <version>3.5.0</version>
</dependency>

springBoot的yml配置:

spring:
    datasource:
    druid: # Druid 【监控】相关的全局配置
      web-stat-filter:
        enabled: true
      stat-view-servlet:
        enabled: true
        allow: # 设置白名单,不填则允许所有访问
        url-pattern: /druid/*
        login-username: admin # 控制台管理用户名和密码
        login-password: 123456
      filter:
        stat:
          enabled: true
          log-slow-sql: true # 慢 SQL 记录
          slow-sql-millis: 100
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
    dynamic: # 多数据源配置
      druid: # Druid 【连接池】相关的全局配置
        initial-size: 5 # 初始连接数
        min-idle: 10 # 最小连接池数量
        max-active: 20 # 最大连接池数量
        max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒
        time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒
        min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒
        max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒
        #        validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: userDb
      datasource:
        userDb:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://rm-1.mysql.rds.aliyuncs.com:3306/a?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
          username: aa
          password: bb
        productDb:
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://rm-2.mysql.rds.aliyuncs.com:3306/b?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
          username: aa
          password: bb

另外,如果有自己的Druid配置类,请注释,怕有冲突。我的就有,注释这个类就好了

测试实现类A:我采用@Ds注解在service方法上面


@Transactional //本类下面的所有方法开始事务机制 
@Service(value = "userBaseInfoDynamic")
@DS("userDb")//这里用了 以下方法默认都是这个,方法也可以单独定义
public class UserBaseInfoDynamicImpl implements UserBaseInfoDynamic {

    @Autowired
    UserBaseInfoMapper userBaseInfoMapper;

    @Resource
    ShopItemInfoMapper shopItemInfoMapper;

    @Resource(name = "userBaseInfoDynamicProduct")
    UserBaseInfoDynamic userBaseInfoDynamicProduct;

    @DS("userDb")
    @Override
    public String selectNickName(long userId) {
        UserBaseInfo userBaseInfo = userBaseInfoMapper.selectByPrimaryKey(userId);
        return userBaseInfo == null ? null : userBaseInfo.getNickName();
    }

    @DS("productDb")
    @Override
    public String selectProductName(long itemId) {
        ShopItemInfo shopItemInfo = shopItemInfoMapper.selectByPrimaryKey(itemId);
        return shopItemInfo == null ? null : shopItemInfo.getItemName();
    }

    @DS("productDb")
    @Override
    public boolean updateItem(long itemId,String itemName) {
        ShopItemInfo shopItemInfo = new ShopItemInfo();
        shopItemInfo.setId(itemId);
        shopItemInfo.setItemName(itemName);
        int i = shopItemInfoMapper.updateByPrimaryKeySelective(shopItemInfo);
        return i == 1;
    }

    @DS("userDb")
    @Override
    public boolean updateItemUserDb(long itemId,String itemName) {
        ShopItemInfo shopItemInfo = new ShopItemInfo();
        shopItemInfo.setId(itemId);
        shopItemInfo.setItemName(itemName);
        int i = shopItemInfoMapper.updateByPrimaryKeySelective(shopItemInfo);
        return i == 1;
    }
    @DS("userDb")
    @Override
    public boolean updateItemAll(long itemId,String itemName){
        //以下要单独调用另外一个bean的方法,调用自己的没用,因为单个service的方法不支持垮数据源
        System.out.println(userBaseInfoDynamicProduct.updateItem(itemId,itemName));
        System.out.println(updateItemUserDb(itemId,itemName));
        //int i = 1/0;
        return true;
    }

以上关键核心点:

userBaseInfoDynamicProduct.updateItem需要搞一个另外service封装调用,调用自己updateItem方法的没用,哪怕指定另外一个数据源,因为单个service方法里面不能垮数据源,只能通过另外的bean调用

测试实现类B:


@Transactional //本类下面的所有方法开始事务机制
@Service(value = "userBaseInfoDynamicProduct")
@DS("productDb")
public class UserBaseInfoDynamicImplProduct implements UserBaseInfoDynamic {

    @Autowired
    UserBaseInfoMapper userBaseInfoMapper;

    @Resource
    ShopItemInfoMapper shopItemInfoMapper;

    @DS("productDb")
    @Override
    public String selectNickName(long userId) {
        UserBaseInfo userBaseInfo = userBaseInfoMapper.selectByPrimaryKey(userId);
        return userBaseInfo == null ? null : userBaseInfo.getNickName();
    }

    @DS("productDb")
    @Override
    public String selectProductName(long itemId) {
        ShopItemInfo shopItemInfo = shopItemInfoMapper.selectByPrimaryKey(itemId);
        return shopItemInfo == null ? null : shopItemInfo.getItemName();
    }

    @DS("productDb")
    @Override
    public boolean updateItem(long itemId,String itemName) {
        ShopItemInfo shopItemInfo = new ShopItemInfo();
        shopItemInfo.setId(itemId);
        shopItemInfo.setItemName(itemName);
        int i = shopItemInfoMapper.updateByPrimaryKeySelective(shopItemInfo);
        return i == 1;
    }

    @Override
    public boolean updateItemUserDb(long itemId, String itemName) {
        return false;
    }

    @Override
    public boolean updateItemAll(long itemId, String itemName) {
        return false;
    }

}

接口:


public interface UserBaseInfoDynamic {
    String selectNickName(long userId);

    String selectProductName(long itemId);



    boolean updateItem(long itemId,String itemName);

    boolean updateItemUserDb(long itemId,String itemName);

    boolean updateItemAll(long itemId,String itemName);

}

单元测试:


@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = CarownerhomeWxMiniprogramApplication.class)
public class UserBaseInfoServiceDynamicTest {
    @Resource(name = "userBaseInfoDynamic")
    UserBaseInfoDynamic userBaseInfoDynamic;

    @Resource(name = "userBaseInfoDynamicProduct")
    UserBaseInfoDynamic userBaseInfoDynamicProduct;

    @Resource
    BusinessTypeInfoService businessTypeInfoService;
    @Test
    public void testDynamic() {
        String nickName = userBaseInfoDynamic.selectNickName(10001);
        System.out.println("从user库获取的昵称:"+nickName);
        String productName = userBaseInfoDynamic.selectProductName(1028);
        System.out.println("从user库获取的商品名称:"+productName);


        String nickName2 = userBaseInfoDynamicProduct.selectNickName(10001);
        System.out.println("从product库获取的昵称:"+nickName2);
        String productName2 = userBaseInfoDynamicProduct.selectProductName(1028);
        System.out.println("从product库获取的商品名称:"+productName2);


        List<BusinessTypeInfo> businessTypeInfos = businessTypeInfoService.selectList(1001l);
        System.out.println("商家类型:"+businessTypeInfos.get(0).getBusinessTypeName());


        userBaseInfoDynamic.updateItemAll(1028,"测试多数据源事务商品");
    }

}
DSTransactional使用场景:如果一个service方法里面调用两个不同mapper的数据源(@DS在mapper类上面)
   
   /**这里如果用@Transactional,
   那么slaveMapper还是访问的masterMapper初次的数据源
   **/
    @DSTransactional 
    public void Add(){
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        masterMapper.insertUser(user);
        Role role = new Role();
        role.setRole("管理员");
        slaveMapper.insertRole(role);
        }
@Mapper
@DS("master")
public interface MasterMapper {
    int insertUser(User user);
}
@Mapper
@DS("slave")
public interface SlaveMapper {
    int insertRole(Role role);
}

<think>好的,我现在需要帮用户解答如何实现分布式系统中的多数据源查询。用户提到了引用1、2、3,这些是关于多数据源的静态配置和动态切换,以及Python在分布式系统中的应用。首先,我应该先理解用户的问题:他们想知道在分布式系统中如何实现多数据源的查询,有哪些方法? 根据引用1,静态数据源是通过不同的包路径定义多个数据源,用户可以根据需要选择。而引用3提到了使用dynamic-datasource-spring-boot-starter这个包来配置多数据源,可能属于动态切换的一种。不过引用1提到静态方式没有使用AbstractRoutingDataSource,而动态方式可能需要这个类。 接下来,我需要整理实现多数据源查询的常见方式。一般来说,有静态数据源配置和动态数据源路由两种方法。静态方式可能是在代码中显式指定不同的数据源,比如不同的DAO层使用不同的数据源,每个数据源对应特定的包路径。这种方式简单,但不够灵活,适合数据源不经常变化的场景。 动态数据源路由则更灵活,可以根据上下文动态切换数据源,比如使用AOP在方法调用前切换数据源。这里可能需要用到AbstractRoutingDataSource,如引用3中的工具可能基于此实现。不过用户提供的引用1中的例子没有使用这个类,而是静态配置。需要区分这两种方法。 另外,分布式系统中还可能涉及分分表策略,比如垂直分和水平分,这也是多数据源管理的一部分。同时,中间件如ShardingSphere可以自动处理数据路由,可能用户需要了解这方面的内容。 然后,我需要结合用户提供的引用内容。引用3中的Maven依赖是Spring Boot中配置多数据源的一个例子,使用的是dynamic-datasource-spring-boot-starter,这应该是一个第三方,帮助简化动态数据源的配置。而引用1中的静态方式则是通过包路径来划分数据源,比如不同的Mapper接口放在不同的包下,每个包对应一个数据源。 在实现步骤方面,静态配置可能需要定义多个DataSource bean,并为每个数据源指定不同的配置参数,然后在不同的Repository或Mapper中使用对应的数据源。动态方式则需要继承AbstractRoutingDataSource,并重写determineCurrentLookupKey方法,根据当前线程的上下文决定使用哪个数据源。 此外,事务管理在多数据源情况下需要特别注意,可能需要使用分布式事务管理器,如JTA或Seata,来保证数据源的事务一致性。不过用户的问题主要在于查询实现,事务可能不是重点,但需要提及。 还要考虑连接池的配置,每个数据源应有自己的连接池,避免资源竞争。同时,在分布式系统中,可能需要考虑数据一致性、延迟等问题,但用户的问题更偏向于如何实现查询,而不是分布式系统的其他挑战。 总结下来,实现方式包括: 1. 静态多数据源配置:显式定义多个数据源,按包或注解指定使用哪个数据源。 2. 动态数据源路由:通过AOP和AbstractRoutingDataSource动态切换数据源。 3. 分分表中间件:使用如ShardingSphere自动路由查询。 4. 编程式切换:在代码中手动切换数据源。 然后,我需要将这些方法详细说明,并结合用户提供的引用,尤其是引用1和3中的内容。例如,引用3中的依赖配置是动态数据源的一种实现,而引用1中的静态方式则是另一种。 最后,生成相关问题,可能涉及性能优化、事务管理、具体框架的使用等。</think>### 多数据源查询实现方式详解 在分布式系统中,多数据源查询的实现通常分为**静态配置**和**动态路由**两类方案,结合分分表策略与事务管理,可满足不同场景需求。 --- #### 一、静态多数据源配置 **核心思路**:显式定义多个数据源,通过包路径或注解固定绑定数据源。 **实现步骤**: 1. **定义多个数据源**:在配置文件中声明不同数据源的连接参数。 ```yaml spring: datasource: db1: url: jdbc:mysql://localhost:3306/db1 username: root password: 123456 db2: url: jdbc:mysql://localhost:3306/db2 username: root password: 123456 ``` 2. **按包路径分配数据源**:如将`com.example.mapper.db1`和`com.example.mapper.db2`的Mapper分别绑定到`db1`和`db2`[^1]。 3. **编程式切换**:在代码中手动注入`DataSource`对象并执行查询。 **适用场景**:数据源数量固定且切换频率低。 --- #### 二、动态数据源路由 **核心思路**:通过`AbstractRoutingDataSource`动态决定当前线程使用的数据源。 **实现步骤**: 1. **引入依赖**:如使用`dynamic-datasource-spring-boot-starter`简化配置[^3]: ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.0</version> </dependency> ``` 2. **继承`AbstractRoutingDataSource`**:重写`determineCurrentLookupKey()`方法,返回当前数据源标识(如`db1`或`db2`)。 3. **AOP切面切换**:通过自定义注解或规则,在方法执行前动态设置数据源标识。 **示例代码**: ```java @DataSource("db1") // 自定义注解 public List<User> getUsersFromDB1() { return userMapper.selectList(); } ``` --- #### 三、分分表中间件方案 **核心工具**: - **ShardingSphere**:自动解析SQL,根据分片键路由到对应数据源。 - **MyCAT**:代理层实现分分表逻辑。 **分策略**: - **垂直分**:按业务模块拆分(如订单、用户)。 - **水平分**:按数据量拆分(如按用户ID哈希分布)。 --- #### 四、事务管理 **挑战**:数据源操作需保证事务一致性。 **解决方案**: 1. **两阶段提交(2PC)**:通过JTA或Seata实现分布式事务。 2. **最终一致性**:结合消息队列补偿(如RocketMQ事务消息)。 --- ### 关键问题与优化 1. **连接池管理**:为每个数据源配置独立连接池(如HikariCP),避免资源竞争。 2. **性能监控**:跟踪各数据源的查询延迟与错误率。 3. **读写分离**:主处理写操作,从处理读操作,提升吞吐量[^2]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值