详细分析MybatisPLus中@DS切换数据源的基本知识(附Demo)

您所在的位置:网站首页 积分中的ds什么意思 详细分析MybatisPLus中@DS切换数据源的基本知识(附Demo)

详细分析MybatisPLus中@DS切换数据源的基本知识(附Demo)

2024-07-10 18:33| 来源: 网络整理| 查看: 265

目录 前言1. 基本知识2. 源码分析3. Demo3.1 成功案例3.2 失败案例 4. 实战

前言

Java项目中多个数据源,相应配置拿些方法哪些类访问 类似JDBC每个类都要写一遍会比较冗余,有没有集中式管理呢??

看这篇文章之前推荐阅读:

java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)jdbc从入门到精通(全)【Java项目】实战CRUD的功能整理(持续更新)

对于DS注解切换数据源,踩了很多坑,一一尝试之后,对此总结其基本知识

对于DS注解的基本知识推荐阅读:

dynamic-datasource 源码dynamic-datasource 具体说明

尝试了好几次版本,发现一直失效,一开始以为是用错了,但是后面发现一定要经过AOP的切面类,也就是@EnableAspectJAutoProxy(exposeProxy = true)这种方式

在这里插入图片描述

1. 基本知识

动态数据源(Dynamic Datasource)是指在运行时根据不同的条件动态切换数据源的技术

在分布式系统、微服务架构和多租户系统中尤为重要

数据源:数据库连接信息的集合,通常包含数据库的URL、用户名、密码、驱动类等

动态数据源:根据业务逻辑或其他条件动态切换到不同的数据源,以实现读写分离、负载均衡、容灾备份等功能

其应用场景有如下:

读写分离:将读操作和写操作分配到不同的数据库上,提高系统的读写性能负载均衡:通过多数据源分担负载,防止单个数据库过载多租户系统:为每个租户配置独立的数据源,隔离数据,保障安全性容灾备份:在主数据库出现故障时,自动切换到备份数据库,保证系统的高可用性

再来说说DS注解的基本概念:

@DS注解用于动态数据源切换,用于指定方法或类使用特定的数据源 @DS注解一般来自MyBatis-Plus中的Dynamic DataSource模块,它允许在方法级别进行数据源切换

需要注意下核心的功能:

方法级别的数据源切换:@DS注解可以直接应用在方法上,使该方法在执行时使用指定的数据源类级别的数据源切换:@DS注解可以应用在类上,使该类中的所有方法在执行时使用指定的数据源优先级:方法级别的@DS注解优先于类级别的@DS注解

引入对应的依赖:

com.baomidou dynamic-datasource-spring-boot-starter ${version}

配置对应的数据源:

spring: datasource: dynamic: primary: master strict: false datasource: master: url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave_1: url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave_2: url: ENC(xxxxx) username: ENC(xxxxx) password: ENC(xxxxx) driver-class-name: com.mysql.jdbc.Driver

基本的切换数据源方式如下:

@Service @DS("slave") public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; @Override @DS("slave_1") public List selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); } } 2. 源码分析

查看其源码:

package com.baomidou.dynamic.datasource.annotation; import java.lang.annotation.*; /** * The core Annotation to switch datasource. It can be annotate at class or method. * * @author TaoYu Kanyuxia * @since 1.0.0 */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface DS { /** * groupName or specific database name or spring SPEL name. * * @return the database you want to switch */ String value(); }

@Target({ElementType.TYPE, ElementType.METHOD}):指定了注解 @DS 可以应用在哪些地方,指定了 @DS 注解可以应用在类和方法上

@Retention(RetentionPolicy.RUNTIME):指定了注解 @DS 在程序运行时保留,可以通过反射来访问并解析 @DS 注解的信息

@Documented:标记这个注解是应该被 javadoc 工具记录的,当生成 API 文档时,如果一个类用 @Documented 注解修饰,那么它的注解将出现在生成的文档中

String value();:属性,返回一个字符串,用于指定要切换到的数据库的名称

3. Demo

对应的版本号此处使用的4.3.0

com.baomidou dynamic-datasource-spring-boot-starter 4.3.0

其数据源配置如下:

spring: datasource: dynamic: # 多数据源配置 druid: # Druid 【连接池】相关的全局配置 initial-size: 1 # 初始连接数 min-idle: 1 # 最小连接池数量 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 primary: master datasource: master: name: manong url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.master.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.master.name} # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.master.name} # SQLServer 连接的示例 # url: jdbc:dm://10.211.55.4:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 username: root password: root # username: sa # password: JSm:g(*%lU4ZAkz06cd52KqT3)i1?H7W # username: SYSDBA # DM 连接的示例 # password: SYSDBA # DM 连接的示例 slave: # 模拟从库,可根据自己需要修改 name: manong lazy: true # 开启懒加载,保证启动速度 url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/${spring.datasource.dynamic.datasource.slave.name}?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/${spring.datasource.dynamic.datasource.slave.name} # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=${spring.datasource.dynamic.datasource.slave.name} # SQLServer 连接的示例 username: root password: root

在写代码的时候有两个注意事项:

单机 + 单数据源:@Transactional 注解单机 + 多数据源:@DSTransactional 注解(新版本支持,旧版本可以使用@Transactional(propagation = Propagation.REQUIRES_NEW))多机 + 单/多数据源:Seata 分布式事务 3.1 成功案例

基本的代码如下:

直接在实现类中使用DS注解 @Service @Validated public class GoodsStoragePlanServiceImpl implements GoodsStoragePlanService { @Autowired private JdbcTemplate oracleJdbcTemplate; @DS("slave") @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void selectGoodsStoragePlan() { String sql = "SELECT * FROM C_OPER_PATH"; oracleJdbcTemplate.queryForList(sql); System.out.println(oracleJdbcTemplate.queryForList(sql)); } } 可通过其他实现类来实现: @Service @Validated public class GoodsStoragePlanServiceImpl implements GoodsStoragePlanService { @Autowired private JdbcTemplate oracleJdbcTemplate; @Autowired private EnterpriseRegistryServiceImpl enterpriseRegistryServiceimpl; @DS("slave") @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public void selectGoodsStoragePlan() { enterpriseRegistryServiceimpl.selectGoodsStoragePlan(); } }

其他实现类:

@Service @Validated public class EnterpriseRegistryServiceImpl implements EnterpriseRegistryService { @Autowired private JdbcTemplate oracleJdbcTemplate; @DS("tos200") @Transactional(propagation = Propagation.REQUIRES_NEW) public void selectGoodsStoragePlan() { String sql = "SELECT * FROM C_OPER_PATH"; oracleJdbcTemplate.queryForList(sql); System.out.println(oracleJdbcTemplate.queryForList(sql)); } } 3.2 失败案例

不可在impl中再次调用另外一个DS注解的方法,AOP切面类无法执行成功

@Service @Validated public class GoodsStoragePlanServiceImpl implements GoodsStoragePlanService { @Autowired private JdbcTemplate oracleJdbcTemplate; @Override public void selectGoodsStoragePlan() { select(); } @DS("slave") @Transactional(propagation = Propagation.REQUIRES_NEW) publivc void select(){ String sql = "SELECT * FROM C_OPER_PATH"; oracleJdbcTemplate.queryForList(sql); System.out.println(oracleJdbcTemplate.queryForList(sql)); } }

切记,必须只有经过AOP的切面才可执行成功,否则单纯加个注解DS,不一定能成功

另外一种动态切换数据源的方式推荐阅读:详细分析Java中DynamicDataSourceContextHolder动态数据源切换(附Demo)

4. 实战

以下实战与上述Demo类似,只不过用在了方法上 (与正常开发一样,只不过在此处添加了该注解)

@Service @DS("slave") @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW) public class TemperatureAlarmHistServiceImpl implements ITemperatureAlarmHistService { @Resource private TemperatureAlarmHistMapper temperatureAlarmHistMapper; public boolean saveForList(List temperatureAlarmHistList) { return temperatureAlarmHistMapper.saveForList(temperatureAlarmHistList); } }

对应的数据源配置如下:

#数据源配置 spring: # 排除DruidDataSourceAutoConfigure autoconfigure: exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure datasource: dynamic: # 设置默认的数据源或者数据源组,默认值即为master primary: master datasource: master: url: jdbc:mysql://localhost:3306/db_master username: root password: root slave: url: jdbc:oracle:manong:@//localhost:1521/GIS username: root password: ROOTGPS1 ep: url: jdbc:sqlserver://localhost:1433;databaseName=manong username: root password: root

提供的配置中,exclude 属性指定了要排除的自动配置类 com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure

这样做的目的是排除Druid连接池的自动配置,避免它与手动配置的数据源发生冲突

另外一种排除的方式:

@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }

前提是 上述这两种方式本身已经引入了该依赖包

com.alibaba druid-spring-boot-starter 1.2.1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3