赞
踩
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
其支持 Jdk 1.7+, SpringBoot 1.5.x 2.x.x 3.x.x。
第一步:pom.xml 引入dynamic-datasource-spring-boot-starter、
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
- <version>3.5.1</version>
- </dependency>
第二步:application.yml 配置多数据源
- spring:
- datasource:
- dynamic:
- primary: master #设置默认的数据源或者数据源组,默认值即为master
- strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
- datasource:
- master:
- url: jdbc:mysql://192.168.43.10:3306/bill?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
- username: root
- password: 123456
- driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
- slave_1:
- url: jdbc:mysql://192.168.43.10:3306/usc?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
- username: root
- password: 123456
- driver-class-name: com.mysql.cj.jdbc.Driver
第三步:使用@DS注解,切换数据源
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
注解 | 结果 |
---|---|
没有@DS | 默认数据源 |
@DS("dsName") | dsName可以为组名也可以为具体某个库的名称 |
在application.yml 配置文件中添加Hikari数据库连接池配置。
- spring:
- datasource:
- dynamic:
- primary: master #设置默认的数据源或者数据源组,默认值即为master
- strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
- datasource:
- master:
- url: jdbc:mysql://192.168.43.10:3306/bill?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
- username: root
- password: 123456
- driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
- slave_1:
- url: jdbc:mysql://192.168.43.10:3306/usc?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
- username: root
- password: 123456
- driver-class-name: com.mysql.cj.jdbc.Driver
- hikari:
- minimum-idle: 10
- maximum-pool-size: 20
- idle-timeout: 500000
- max-lifetime: 540000
- connection-timeout: 60000
- connection-test-query: select 1
dynamic-datasource 支持如下数据库连接池:beecp\dbcp2\druid\hikair 等常用数据库连接池。
Beecp 通用配置:
- beecp:
- fairness: true
- initial-size: 5
- max-active: 20
- idleTimeout: 60000
- connectionTestSql: SELECT 1
dbcp2 通用配置:
- dbcp2:
- initial-size:5
- min-idle:5
- max-idle:10
- max-open-prepared-statements:100
druid 通用配置:
- druid:
- # 初始化大小
- initial-size: 5
- # 最小连接数
- min-idle: 10
- # 最大连接数
- max-active: 20
- # 获取连接时的最大等待时间
- max-wait: 60000
- # 一个连接在池中最小生存的时间,单位是毫秒
- min-evictable-idle-time-millis: 300000
- # 多久才进行一次检测需要关闭的空闲连接,单位是毫秒
- time-between-eviction-runs-millis: 60000
- # 配置扩展插件:stat-监控统计,log4j-日志,wall-防火墙(防止SQL注入),去掉后,监控界面的sql无法统计
- filters: stat,wall
- # 检测连接是否有效的 SQL语句,为空时以下三个配置均无效
- validation-query: SELECT 1
- # 申请连接时执行validationQuery检测连接是否有效,默认true,开启后会降低性能
- test-on-borrow: true
- # 归还连接时执行validationQuery检测连接是否有效,默认false,开启后会降低性能
- test-on-return: true
- # 申请连接时如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效,默认false,建议开启,不影响性能
- test-while-idle: true
- # 是否开启 StatViewServlet
- stat-view-servlet:
- enabled: true
- # 访问监控页面 白名单,默认127.0.0.1
- allow: 127.0.0.1
- login-username: admin
- login-password: admin
- # FilterStat
- filter:
- stat:
- # 是否开启 FilterStat,默认true
- enabled: true
- # 是否开启 慢SQL 记录,默认false
- log-slow-sql: true
- # 慢 SQL 的标准,默认 3000,单位:毫秒
- slow-sql-millis: 5000
- # 合并多个连接池的监控数据,默认false
- merge-sql: false
hikair 通用配置:
- hikari:
- minimum-idle: 10
- maximum-pool-size: 20
- idle-timeout: 500000
- max-lifetime: 540000
- connection-timeout: 60000
- connection-test-query: select 1
温馨提示:上述配置对象属性值与 dynamic-datasource定义的属性值对象可能存在差异,请以:
com.baomidou.dynamic.datasource.spring.boot.autoconfigure.* 相关配置类对象为主
在任意类中依赖DataSource 接口类,检查输出的接口实例类是否为:com.baomidou.dynamic.datasource.DynamicRoutingDataSource。
示例代码:
- package com.zzg.controller;
-
-
- import com.zzg.entity.BaseProjectPO;
- import com.zzg.service.IBaseProjectService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PathVariable;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.sql.DataSource;
- import java.util.List;
-
- @RestController
- @RequestMapping("/baseProject")
- public class BaseProjectController {
-
- @Autowired
- private DataSource dataSource;
-
- @Autowired
- private IBaseProjectService service;
-
- @GetMapping("/list")
- public List<BaseProjectPO> list() {
- System.out.println(dataSource.getClass());
- return service.list();
- }
-
- @GetMapping("{id}")
- public BaseProjectPO getBaseProjectInfo(@PathVariable String id){
- return service.getById(id);
- }
- }
在list 逻辑方法中,输出DataSource 接口实例对象为:com.baomidou.dynamic.datasource.DynamicRoutingDataSource
在application.yml 和Application 程序入口添加如下配置:
application.yml
- mybatis-plus:
- mapper-locations: classpath:mapper/*.xml
- type-aliases-package: cn.zzg.entity
Application
- package com.zzg;
-
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @SpringBootApplication
- @MapperScan({"com.zzg.mapper"})
- public class Application {
- public static void main(String[] args) {
- SpringApplication.run(Application.class, args);
- }
- }
示例:
1、定义数据源Model
- import io.swagger.annotations.ApiModelProperty;
- import lombok.Data;
- import javax.validation.constraints.NotBlank;
- @Data
- public class DataSourceDTO {
- @NotBlank
- @ApiModelProperty(value = "连接池名称", example = "db1")
- private String poolName;
- @NotBlank
- @ApiModelProperty(value = "JDBC driver", example = "com.mysql.cj.jdbc.Driver")
- private String driverClassName;
- @NotBlank
- @ApiModelProperty(value = "JDBC url 地址", example = "jdbc:mysql://x.x.x.x:3306/x?useUnicode=true&characterEncoding=utf-8")
- private String url;
- @NotBlank
- @ApiModelProperty(value = "JDBC 用户名", example = "sa")
- private String username;
- @ApiModelProperty(value = "JDBC 密码")
- private String password;
- }
2、Controller 定义添加和移除数据源
- import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
- import com.baomidou.dynamic.datasource.creator.*;
- import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
- import com.zzg.common.DataSourceDTO;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.beans.BeanUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.validation.annotation.Validated;
- import org.springframework.web.bind.annotation.*;
-
- import javax.sql.DataSource;
- import java.util.Set;
-
- @RestController
- @RequestMapping("/datasources")
- @Api(tags = "添加删除数据源")
- public class DataSourceController {
-
- @Autowired
- private DataSource dataSource;
- @Autowired
- private DefaultDataSourceCreator dataSourceCreator;
- @Autowired
- private DruidDataSourceCreator druidDataSourceCreator;
- @Autowired
- private HikariDataSourceCreator hikariDataSourceCreator;
-
- @GetMapping
- @ApiOperation("获取当前所有数据源")
- public Set<String> now() {
- DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
- return ds.getDataSources().keySet();
- }
-
- //通用数据源会根据maven中配置的连接池根据顺序依次选择。
- //默认的顺序为druid>hikaricp>beecp>dbcp>spring basic
- @PostMapping("/add")
- @ApiOperation("通用添加数据源(推荐)")
- public Set<String> add(@Validated @RequestBody DataSourceDTO dto) {
- DataSourceProperty dataSourceProperty = new DataSourceProperty();
- BeanUtils.copyProperties(dto, dataSourceProperty);
- DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
- DataSource dataSource = dataSourceCreator.createDataSource(dataSourceProperty);
- ds.addDataSource(dto.getPoolName(), dataSource);
- return ds.getDataSources().keySet();
- }
-
- @PostMapping("/addDruid")
- @ApiOperation("基础Druid数据源")
- public Set<String> addDruid(@Validated @RequestBody DataSourceDTO dto) {
- DataSourceProperty dataSourceProperty = new DataSourceProperty();
- BeanUtils.copyProperties(dto, dataSourceProperty);
- dataSourceProperty.setLazy(true);
- DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
- DataSource dataSource = druidDataSourceCreator.createDataSource(dataSourceProperty);
- ds.addDataSource(dto.getPoolName(), dataSource);
- return ds.getDataSources().keySet();
- }
-
- @PostMapping("/addHikariCP")
- @ApiOperation("基础HikariCP数据源")
- public Set<String> addHikariCP(@Validated @RequestBody DataSourceDTO dto) {
- DataSourceProperty dataSourceProperty = new DataSourceProperty();
- BeanUtils.copyProperties(dto, dataSourceProperty);
- dataSourceProperty.setLazy(true);//3.4.0版本以下如果有此属性,需手动设置,不然会空指针。
- DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
- DataSource dataSource = hikariDataSourceCreator.createDataSource(dataSourceProperty);
- ds.addDataSource(dto.getPoolName(), dataSource);
- return ds.getDataSources().keySet();
- }
-
- @DeleteMapping
- @ApiOperation("删除数据源")
- public String remove(String name) {
- DynamicRoutingDataSource ds = (DynamicRoutingDataSource) dataSource;
- ds.removeDataSource(name);
- return "删除成功";
- }
- }
第一步:使用框架自带加密工具类com.baomidou.dynamic.datasource.toolkit.CryptoUtils对需要加密的字符串进行加密。
- @Test
- void test() throws Exception {
- String passWord= CryptoUtils.encrypt("123456");
- System.out.println(passWord);
- // 输出加密密码
- //Y3ycHCcZGa+N+OK+qXTWA0gJ1L1N+FYrswTgRQEegdKVTefiujYxjlytR6zOuV5Y3AifL/P10yWshYKQaqpkkQ==
- }
第二步:修改配置文件中的相关密码
- server:
- port: 8080
- spring:
- datasource:
- dynamic:
- primary: master #设置默认的数据源或者数据源组,默认值即为master
- strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
- datasource:
- master:
- url: jdbc:mysql://192.168.43.10:3306/bill?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
- username: root
- password: ENC(Y3ycHCcZGa+N+OK+qXTWA0gJ1L1N+FYrswTgRQEegdKVTefiujYxjlytR6zOuV5Y3AifL/P10yWshYKQaqpkkQ==)
- driver-class-name: com.mysql.cj.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
- slave_1:
- url: jdbc:mysql://192.168.43.10:3306/usc?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&autoReconnect=true
- username: root
- password: ENC(Y3ycHCcZGa+N+OK+qXTWA0gJ1L1N+FYrswTgRQEegdKVTefiujYxjlytR6zOuV5Y3AifL/P10yWshYKQaqpkkQ==)
- driver-class-name: com.mysql.cj.jdbc.Driver
- hikari:
- minimum-idle: 10
- maximum-pool-size: 20
- idle-timeout: 500000
- max-lifetime: 540000
- connection-timeout: 60000
- connection-test-query: select 1
- mybatis-plus:
- mapper-locations: classpath:mapper/*.xml
- type-aliases-package: cn.zzg.entity
通过上面的学习,我们已经学习了解到了@DS 注解,基于此注解我们可以快速实现简单版本的读写分离。
- package com.zzg.mapper;
-
- import com.baomidou.dynamic.datasource.annotation.DS;
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.zzg.entity.BaseArch;
- import org.apache.ibatis.annotations.Insert;
- import org.apache.ibatis.annotations.Mapper;
- import org.apache.ibatis.annotations.Param;
- import org.apache.ibatis.annotations.Select;
-
- @Mapper
- public interface BaseArchMapper extends BaseMapper<BaseArch> {
-
- @DS("master")
- int addUser(BaseArch entity);
-
- @DS("slave_1")
- BaseArch findUser(@Param("id") String id);
- }
不过这种方式有点繁琐,每个Mapper都需要添加注解。我们 可以通过MyBatis 拦截器 + 手动切换数据源实现读写分离。
定义:读写分离MyBatis 拦截器
- @Intercepts({@Signature(
- type = Executor.class,
- method = "query",
- args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
- ), @Signature(
- type = Executor.class,
- method = "query",
- args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
- ), @Signature(
- type = Executor.class,
- method = "update",
- args = {MappedStatement.class, Object.class}
- )})
- @Component
- @Primary
- public class MasterSlaveAutoRoutingPlugin implements Interceptor {
-
- private static final String MASTER = "master";
-
- private static final String SLAVE = "slave_1";
-
- @Override
- public Object intercept(Invocation invocation) throws Throwable {
- Object[] args = invocation.getArgs();
- MappedStatement ms = (MappedStatement) args[0];
- try {
- DynamicDataSourceContextHolder.push(SqlCommandType.SELECT == ms.getSqlCommandType() ? SLAVE : MASTER);
- return invocation.proceed();
- } finally {
- DynamicDataSourceContextHolder.clear();
- }
- }
-
- @Override
- public Object plugin(Object target) {
- return target instanceof Executor ? Plugin.wrap(target, this) : target;
- }
-
- @Override
- public void setProperties(Properties properties) {
- }
- }
至此,我们可以移除Controller/Service/Dao/Mapper 上得@DS 注解。
请参考文章:多数据源切换[dynamic-datasource] 手动切换数据源
通过 mybatis 的拦截器 + 手动切换数据源实现了读写分离,同时 dynamic-datasource
还为我们提供了负载的效果,同一个组下的默认就是负载均衡效果,怎么才是同一个组呢,上面有提到只需以下划线 _ 分割即可,下面修改配置文件:
- spring:
- datasource:
- dynamic:
- primary: db_1 #设置默认的数据源或者数据源组,默认值即为master
- strict: true #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
- datasource:
- db_1:
- driver-class-name: com.mysql.cj.jdbc.Driver
- type: com.alibaba.druid.pool.DruidDataSource
- url: jdbc:mysql://192.168.43.10:3306/db1?useUnicode=true&characterEncoding=utf8
- username: root
- password: 123456
- db_2:
- driver-class-name: com.mysql.cj.jdbc.Driver
- type: com.alibaba.druid.pool.DruidDataSource
- url: jdbc:mysql://192.168.43.10:3306/db2?useUnicode=true&characterEncoding=utf8
- username: root
- password: 123456
声明 Dao
,指定数据源为 db
:
- @Mapper
- @DS("db")
- public interface UserMapper extends BaseMapper<UserEntity> {
- }
大家都了解在 Spring
中事物使用 @Transactional
注解即可,但是仅针对于单个数据源的情况,多数据源下我们可以使用 jta
来控制或其他事务管理框架,在 dynamic-datasource
中又推出了 @DSTransactional
注解来代替 Spring
的 @Transactional
注解。
- @Slf4j
- @SpringBootTest
- class DynamicDatasourceDemoApplicationTests {
-
- @Autowired
- DB1UserDao db1UserDao;
-
- @Autowired
- DB2UserDao db2UserDao;
-
- @Test
- @DSTransactional
- void test1() {
- UserEntity entity = new UserEntity();
- entity.setName("王五");
- entity.setAge(16);
- int db1 = db1UserDao.insert(entity);
- log.info("db1写入个数:{} ", db1);
- int db2 = db2UserDao.insert(entity);
- log.info("db2写入个数:{} ", db2);
- //模拟异常
- int a = 1 / 0;
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。