当前位置: 首页 > news >正文

mybatis plus 配置多数据源(数据源进行切换)

多数据源(数据源进行切换)

AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源,这样我们可以在执行查询之前,设置使用的数据源。实现可动态路由的数据源,在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey() 决定使用哪个数据源。

1、application.yml中配置多个数据源

# Order
spring.datasource.order.url=jdbc:mysql://localhost:3306/seata_order?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.order.username=root
spring.datasource.order.password=123456
spring.datasource.order.driver-class-name=com.mysql.cj.jdbc.Driver
# Storage
spring.datasource.storage.url=jdbc:mysql://localhost:3306/seata_storage?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.storage.username=root
spring.datasource.storage.password=123456
spring.datasource.storage.driver-class-name=com.mysql.cj.jdbc.Driver
# Pay
spring.datasource.pay.url=jdbc:mysql://localhost:3306/seata_pay?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false
spring.datasource.pay.username=root
spring.datasource.pay.password=123456
spring.datasource.pay.driver-class-name=com.mysql.cj.jdbc.Driver

2、主启动类添加注解

@SpringBootApplication    

@MapperScan("com.example.demo.mapper")

3、编写配置类

@Getter
public enum DataSourceKey {
    /**
     * Order data source key.
     */
    ORDER,
    /**
     * Storage data source key.
     */
    STORAGE,
    /**
     * Pay data source key.
     */
    PAY,
}

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(DataSourceKey.ORDER::name);
    private static List<Object> dataSourceKeys = new ArrayList<>();
    public static void setDataSourceKey(DataSourceKey key) {
        CONTEXT_HOLDER.set(key.name());
    }
    public static String getDataSourceKey() {
        return CONTEXT_HOLDER.get();
    }
    public static void clearDataSourceKey() {
        CONTEXT_HOLDER.remove();
    }
    public static List<Object> getDataSourceKeys() {
        return dataSourceKeys;
    }
}

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        //log.info("当前数据源 [{}]", DynamicDataSourceContextHolder.getDataSourceKey());
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }
}

@Configuration
public class DataSourceProxyConfig {
    @Bean("originOrder")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource dataSourceMaster() {
        return new DruidDataSource();
    }
    @Bean("originStorage")
    @ConfigurationProperties(prefix = "spring.datasource.storage")
    public DataSource dataSourceStorage() {
        return new DruidDataSource();
    }
    @Bean("originPay")
    @ConfigurationProperties(prefix = "spring.datasource.pay")
    public DataSource dataSourcePay() {
        return new DruidDataSource();
    }

    @Bean(name = "order")
    public DataSourceProxy masterDataSourceProxy(@Qualifier("originOrder") DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
    @Bean(name = "storage")
    public DataSourceProxy storageDataSourceProxy(@Qualifier("originStorage") DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
    @Bean(name = "pay")
    public DataSourceProxy payDataSourceProxy(@Qualifier("originPay") DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }
    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource(@Qualifier("order") DataSource dataSourceOrder,
                                        @Qualifier("storage") DataSource dataSourceStorage,
                                        @Qualifier("pay") DataSource dataSourcePay) {
        DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>(3);
        dataSourceMap.put(DataSourceKey.ORDER.name(), dataSourceOrder);
        dataSourceMap.put(DataSourceKey.STORAGE.name(), dataSourceStorage);
        dataSourceMap.put(DataSourceKey.PAY.name(), dataSourcePay);

        dynamicRoutingDataSource.setDefaultTargetDataSource(dataSourceOrder);
        dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
        DynamicDataSourceContextHolder.getDataSourceKeys().addAll(dataSourceMap.keySet());
        return dynamicRoutingDataSource;
    }
    @Bean
    @ConfigurationProperties(prefix = "mybatis-plus")  // MybatisSqlSessionFactoryBean中有各种MybatisPlus的配置属性(globalConfig、mapperLocations} 而SqlSessionFactoryBean中则是mybatis的各种配置属性(typeAlies、mapperLocations) 
    public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DataSource dataSource) {
        // 这里用 MybatisSqlSessionFactoryBean 代替了 SqlSessionFactoryBean,否则 MyBatisPlus 不会生效
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        return mybatisSqlSessionFactoryBean;
    }
}

调用切换数据源:

@GlobalTransactional
@Override
public OperationResponse placeOrder(PlaceOrderRequestVO placeOrderRequestVO) throws Exception {
    DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);//切换数据源
    Integer amount = 1;
    Integer price = placeOrderRequestVO.getPrice();
    Order order = Order.builder().build();
    Integer saveOrderRecord = orderDao.insert(order);
    // 扣减库存
    boolean operationStorageResult = storageService.reduceStock(placeOrderRequestVO.getProductId(), amount);
    // 扣减余额
    boolean operationBalanceResult = payService.reduceBalance(placeOrderRequestVO.getUserId(), price);
    DynamicDataSourceContextHolder.setDataSourceKey(DataSourceKey.ORDER);//切换数据源
    order.setStatus(OrderStatus.SUCCESS);
    Integer updateOrderRecord = orderDao.updateById(order);
    return success(operationStorageResult && operationBalanceResult);
}

项目启动报错:Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured

原因:导入spring-mybatis依赖后,springboot启动时会自动加载数据源,由于dataSource配置成多数据源加载不到spring.datasource.url故而报错。

解决:1、主启动类添加@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

2、若上面配置还是无法解决,可以配置一个默认数据源让其启动时加载(不影响,会被多数据源切换时覆盖的):spring.datasource.url

额外:

SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);

SqlSession sqlSession=factory.openSession(); //sqlSession就是用来操作sql语句的

使用 MyBatis-Spring 之后, 会使用SqlSessionFactoryBean来代替SqlSessionFactoryBuilder创建SqlSessionFactory

MybatisPlus需要使用MybatisSqlSessionFactoryBean。

相关文章:

  • ssm139选课排课系统的设计与开发+vue
  • Pytorch: 解决因pytorch版本不同 导致训练ckpt加载失败
  • FPGA 纯逻辑arinc818 ip core
  • 力扣-2904最短且字典序最小的美丽子序列
  • 【机器学习300问】95、什么是KNN算法?它和K-means什么关系?
  • 网络协议——有状态协议和无状态协议
  • linux下删除nginx进程
  • 自主创新助力科技强军,麒麟信安闪耀第九届军博会
  • 轻松拿捏C语言——【字符串函数】的使用及模拟实现
  • python02 循环与容器
  • DSVPN综合实验
  • 【JAVA基础之网络编程】UDP和TCP协议以及三次握手和四次挥手的过程
  • 【游戏引擎】Unity脚本基础 开启游戏开发之旅
  • Linux完整版命令大全(九)
  • Leecode热题100---55:跳跃游戏(贪心算法)
  • 【腾讯Bugly干货分享】从0到1打造直播 App
  • 2017届校招提前批面试回顾
  • Apache Pulsar 2.1 重磅发布
  • JavaScript中的对象个人分享
  • JSONP原理
  • JS变量作用域
  • Netty源码解析1-Buffer
  • node学习系列之简单文件上传
  • PHP的Ev教程三(Periodic watcher)
  • Python_网络编程
  • vue从入门到进阶:计算属性computed与侦听器watch(三)
  • vue的全局变量和全局拦截请求器
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • Prometheus VS InfluxDB
  • puppet连载22:define用法
  • 阿里云API、SDK和CLI应用实践方案
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​字​节​一​面​
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • #LLM入门|Prompt#3.3_存储_Memory
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • $(this) 和 this 关键字在 jQuery 中有何不同?
  • (03)光刻——半导体电路的绘制
  • (1)SpringCloud 整合Python
  • (Redis使用系列) SpirngBoot中关于Redis的值的各种方式的存储与取出 三
  • (SpringBoot)第二章:Spring创建和使用
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (学习日记)2024.04.04:UCOSIII第三十二节:计数信号量实验
  • (转)EXC_BREAKPOINT僵尸错误
  • .bat批处理(八):各种形式的变量%0、%i、%%i、var、%var%、!var!的含义和区别
  • .dwp和.webpart的区别
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET Framework杂记
  • .NET Standard / dotnet-core / net472 —— .NET 究竟应该如何大小写?
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .NET使用存储过程实现对数据库的增删改查
  • .net下简单快捷的数值高低位切换