springboot2.0实现多数据源,spring boot多个数据源的实例
00-1010前言基于动态数据源的多数据源实现动态数据源特性介绍使用@DS切换数据源@DS使用示例手动实现基于AOP的多数据源摘要
00-1010公司的项目有连接几个不同数据库的需求。经过专门研究,根据在线数据构建了一个基于AOP的数据源切换轮。然而,在继续探索之后,突然发现了一个开源的多数据源管理启动器。然而,本文将介绍这两种方法。
目录
00-1010 Dynamic-data source-Spring-Boot-Starter是一个基于springboot的启动器,可以快速集成多个数据源。
它支持Jdk 1.7+, SpringBoot 1.4.x 1.5.x 2.x.x.
00-1010支持数据源分组,适用于多种场景。它是一种纯多数据库读写分离,一主多从的混合模式。ENC()支持数据库敏感配置信息的加密。支持各数据库独立初始化表结构模式和数据库数据库。支持无数据源启动,支持数据源的惰性加载(必要时创建连接)。支持用户自定义注释,DS(3.2.0)应该继承。并简化德鲁伊、HikariCp、BeeCp、Dbcp2的快速集成。为Mybatis-Plus、Quartz、ShardingJdbc、P6sy、Jndi等组件提供集成解决方案。提供自定义数据源方案(如从数据库加载所有数据)。提供项目启动后动态添加和移除数据源的方案。在Mybatis环境下提供纯读写分离方案。提供一个使用spel动态参数解析数据源的解决方案。内置spel,session,header,支持定制。支持多层数据源的嵌套切换。服务a服务b服务c .提供一个基于seata的分布式交易方案。提供本地多数据源事务方案。附:不能和原生spring事务混用。目前,我们只讨论使用动态数据源进行数据源切换。请自行搜索他人。
动态数据源的相关约定
Dynamic-datasource只做切换数据源的核心事情,不限制你的具体操作。你可以通过切换数据源来做任何CRUD。配置文件中所有数据源的头用下划线分开就是组名,组名相同的数据源会放在一个组下。切换数据源可以是组名或特定的数据源名。切换组名时,使用负载平衡算法进行切换。默认的数据源名称是master,可以通过spring . data source . dynamic . primary修改,方法上的注释优先于类上的注释。DS支持在抽象类上继承DS,但不支持在接口上继承。引入动态数据源依赖关系
依赖关系groupIdcom.baomidou/groupId artifactid dynamic-data source-Spring-Boot-Starter/artifactid version $ { version }/version/依赖关系配置数据源
spring : data source 3360 Dynamic 3360 Primary 3360 MySQL #设置默认数据源或数据源组,默认值为master strict: false #严格匹配数据源。如果默认为false。与指定的数据源不匹配,将引发异常。是非默认数据源data source : MySQL 3360 URL : JDBC 3360 MySQL 3360//xx . xx . xx . xx 33603306/动态用户名: root密码3360 123456 driver-class-name 3360 com . MySQL . JDBC . driver # 3 . 2 . 0开始支持SPI。您可以省略此配置pgsql: url: ENC(
/strong> 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
注解结果不使用@DS注解默认数据源,即primary: mysql@DS(dsName)dsName可以为组名也可以为具体某个库的名称
@DS使用实例
@Service@DS("mysql")public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; // 不使用@DS注解则代表使用默认数据源 // 如果类上存在,则使用类上标注的数据源 public List selectAll() { return jdbcTemplate.queryForList("select * from user"); } @Override @DS("pgsql") // 方法上注解 优先于 类上注解,即使类上标注也优先采用方法上的标注 public List selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); }}
基于AOP手动实现多数据源
本次代码参考 https://github.com/mianshenglee/my-example/tree/master/multi-datasource/dynamic-datasource ,因源码不满足的需求,因此我在此基础做了修改。
项目工程结构
项目依赖
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>me.mason.demo</groupId> <artifactId>dynamic-datasource</artifactId> <version>0.0.1-SNAPSHOT</version> <name>dynamic-datasource</name> <description>Demo project for dynamic datasource</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--spring boot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.9</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
配置文件
server.port=8080server.servlet.context-path=/ddlogging.level.root=INFOlogging.level.me.mason.demo.dynamicdatasource.mapper=DEBUG# mybatis-plusmybatis-plus.type-aliases-package=me.mason.demo.dynamicdatasource.entity# 默认位置,可不配置#mybatis-plus.mapper-locations=classpath*:/mapper/*.xmlmybatis.mapper-locations=classpath*:/mapper/*.xml# 使用数据库自增IDmybatis-plus.global-config.db-config.id-type=auto# masterspring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.master.url=jdbc:mysql://10.0.1.243:3306/scheduling?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8spring.datasource.master.username=rootspring.datasource.master.password=123456# slavespring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.slave.url=jdbc:mysql://10.0.1.243:3306/scheduling1?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8spring.datasource.slave.username=rootspring.datasource.slave.password=123456
自定义注解
// 标记注解可使用在方法与类上@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface DS {// 默认值为MASTER String value() default DataSourceConstants.DS_KEY_MASTER;}
编写DataSourceConstants
/** * 数据源常量 **/public class DataSourceConstants { /** * master数据源 */ public static final String DS_KEY_MASTER = "master"; /** * slave数据源 */ public static final String DS_KEY_SLAVE = "slave";}
动态数据源名称上下文处理
/** * 动态数据源名称上下文处理 **/public class DynamicDataSourceContextHolder { /** * 动态数据源名称上下文 */ private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>(); /** * 设置数据源 * @param key */ public static void setContextKey(String key){ DATASOURCE_CONTEXT_KEY_HOLDER.set(key); } /** * 获取数据源名称 * @return */ public static String getContextKey(){ String key = DATASOURCE_CONTEXT_KEY_HOLDER.get(); return key == null?DataSourceConstants.DS_KEY_MASTER:key; } /** * 删除当前数据源名称 */ public static void removeContextKey(){ DATASOURCE_CONTEXT_KEY_HOLDER.remove(); }}
获取当前动态数据源方法
/** * 动态数据源 **/public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getContextKey(); }}
动态数据源配置
/** * 动态数据源配置 **/@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })@Configuration// 此处我们//@PropertySource("classpath:config/jdbc.properties")@MapperScan(basePackages = "me.mason.demo.dynamicdatasource.mapper")public class DynamicDataSourceConfig { @Bean(DataSourceConstants.DS_KEY_MASTER) // 需要与配置文件中对应 @ConfigurationProperties(prefix = "spring.datasource.master") public DruidDataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(DataSourceConstants.DS_KEY_SLAVE) @ConfigurationProperties(prefix = "spring.datasource.slave") public DruidDataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @Primary public DynamicDataSource dynamicDataSource() { Map<Object, Object> dataSourceMap = new HashMap<>(2); dataSourceMap.put(DataSourceConstants.DS_KEY_MASTER, masterDataSource()); dataSourceMap.put(DataSourceConstants.DS_KEY_SLAVE, slaveDataSource()); //设置动态数据源 DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(dataSourceMap); dynamicDataSource.setDefaultTargetDataSource(masterDataSource()); return dynamicDataSource; }}
AOP切面
/** * 切面 */@Aspect@Component//@Order(-10)public class DynamicDataSourceAspect {// 以在类上使用了@Service作为切入点 @Pointcut("@within(org.springframework.stereotype.Service)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Class<?> aClass = Class.forName(signature.getDeclaringType().getName()); // 方法优先,如果方法上存在注解,则优先使用方法上的注解 if (signature.getMethod().isAnnotationPresent(DS.class)) { DynamicDataSourceContextHolder.setContextKey(signature.getMethod().getAnnotation(DS.class).value()); // 其次类优先,如果类上存在注解,则使用类上的注解 }else if (aClass.isAnnotationPresent(DS.class)) { DynamicDataSourceContextHolder.setContextKey(aClass.getAnnotation(DS.class).value()); // 如果都不存在,则使用默认 } else { DynamicDataSourceContextHolder.setContextKey(DataSourceConstants.DS_KEY_MASTER); } try { return joinPoint.proceed(); } finally { DynamicDataSourceContextHolder.removeContextKey(); } }}
编写TestUser实体
@Data@TableName("test_user")public class TestUser implements Serializable { private static final long serialVersionUID = 1L; /** id */ private Long id; /** 姓名 */ private String name; /** 手机号 */ private String phone; /** 职称职别 */ private String title; /** 邮箱 */ private String email; /** 性别 */ private String gender; /** 出生时间 */ private Date dateOfBirth; /** 1:已删除,0:未删除 */ private Integer deleted; /** 创建时间 */ private Date sysCreateTime; /** 创建人 */ private String sysCreateUser; /** 更新时间 */ private Date sysUpdateTime; /** 更新人 */ private String sysUpdateUser; /** 版本号 */ private Long recordVersion; public TestUser() { }}
TestUserMapper
@Service//@DS(DataSourceConstants.DS_KEY_SLAVE)public class TestUserService { @Autowired private TestUserMapper testUserMapper; /** * 查询master库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getMasterUser(){ QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>(); return testUserMapper.selectAll(queryWrapper.isNotNull("name")); } /** * 查询slave库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getSlaveUser(){ return testUserMapper.selectList(null); }}
TestUserService
@Service//@DS(DataSourceConstants.DS_KEY_SLAVE)public class TestUserService { @Autowired private TestUserMapper testUserMapper; /** * 查询master库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getMasterUser(){ QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>(); return testUserMapper.selectAll(queryWrapper.isNotNull("name")); } /** * 查询slave库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getSlaveUser(){ return testUserMapper.selectList(null); }}
TestUserController
@RestController@RequestMapping("/user")public class TestUserController { @Autowired private TestUserService testUserService; /** * 查询全部 */ @GetMapping("/listall") public Object listAll() { int initSize = 2; Map<String, Object> result = new HashMap<>(initSize); List<TestUser> masterUser = testUserService.getMasterUser(); result.put("masterUser", masterUser); List<TestUser> slaveUser = testUserService.getSlaveUser(); result.put("getSlaveUser", slaveUser); return ResponseResult.success(result); }}
MapperXml
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="me.mason.demo.dynamicdatasource.mapper.TestUserMapper"> <select id="selectAll" resultType="me.mason.demo.dynamicdatasource.entity.TestUser"> select * from test_user <if test="ew!=null"> ${ew.customSqlSegment} </if> </select></mapper>
启动测试
不使用注解
@Service//@DS(DataSourceConstants.DS_KEY_SLAVE)public class TestUserService { @Autowired private TestUserMapper testUserMapper; /** * 查询master库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getMasterUser(){ QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>(); return testUserMapper.selectAll(queryWrapper.isNotNull("name")); } /** * 查询slave库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getSlaveUser(){ return testUserMapper.selectList(null); }}
效果
该代码优先级与使用框架效果一致,即不使用注解将默认使用MASTER数据库,方法上存在注解优先使用方法上标注的注解。
已知MASTER 6条数据, SLAVE4条数据
访问 http://127.0.0.1:8080/dd/user/listall 查看效果
类上使用注解
@Service@DS(DataSourceConstants.DS_KEY_SLAVE)public class TestUserService { @Autowired private TestUserMapper testUserMapper; /** * 查询master库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getMasterUser(){ QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>(); return testUserMapper.selectAll(queryWrapper.isNotNull("name")); } /** * 查询slave库User * @return */// @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getSlaveUser(){ return testUserMapper.selectList(null); }}
效果
方法上使用注解
@Service@DS(DataSourceConstants.DS_KEY_SLAVE)public class TestUserService { @Autowired private TestUserMapper testUserMapper; /** * 查询master库User * @return */ @DS(DataSourceConstants.DS_KEY_SLAVE) public List<TestUser> getMasterUser(){ QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>(); return testUserMapper.selectAll(queryWrapper.isNotNull("name")); } /** * 查询slave库User * @return */ @DS(DataSourceConstants.DS_KEY_MASTER) public List<TestUser> getSlaveUser(){ return testUserMapper.selectList(null); }}
效果
总结
到此这篇关于SpringBoot多数据源的两种实现方式的文章就介绍到这了,更多相关SpringBoot多数据源实现内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。