mybatisplus快速实现动态数据源切换

Posted Blue92120

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatisplus快速实现动态数据源切换相关的知识,希望对你有一定的参考价值。

   1.背景
 

  通常一个系统只需要连接一个数据库就可以了。但是在企业应用的开发中往往会和其他子系统交互,特别是对于一些数据实时性要求比较高的数据,我们就需要做实时连接查询,而不是做同步。这个时候就需要用到多数据源。
 

  举个简单的例子某企业要做订单网上订单系统这里面就可以涉及到多个子系统的连接,比如:产品主数据的数据源,项目管理系统的数据源(项目可以产品订单)等多个不同数据库类似的数据源,他们可能是ORACLE,SQL SERVER,mysql等多种混合数据源。
 

  2.多数据源概述
 

  基于以上的背景,就会选择使用多个数据源,一个数据源用于读一个数据源用于写。或者不同的数据源混合使用。他的基本思想其实就是AOP。我们可以通过AOP的思想实现动态数据源切换,通过这个AOP思想可适用于多种场景、纯粹多库、读写分离、一主多从、混合模式等。

  动态数据源能进行自动切换的核心就是spring底层的AbstractRoutingDataSource进行数据源的路由,只要继承了这个类均可看作是一个数据源的实现。主要实现方法是determineCurrentLookupkey,该方法只需要返回数据源实例名称。
 

  3.mybatisplus多数据源
 

  我们在项目中用mybatisplus的使用用得比较多,这个动态数据源切换需要实现的话,比较麻烦,如果有现成的框架使用则最好不过了。恰好mybatiplus就能实现。文档地址如下:
 

  ```properties

  https://baomidou.com/pages/a61e1b/#%E6%96%87%E6%A1%A3-documentation

  https://www.kancloud.cn/tracy5546/dynamic-datasource/2264611

  ```

  4.使用
 

  4.1介绍
 

  dynamic-datasource-spring-boot-starter是一个基于springboot的快速集成多数据源的启动器。
 

  特性:

  -支持**数据源分组**,适用于多种场景纯粹多库读写分离一主多从混合模式。

  -支持数据库敏感配置信息**加密**ENC。

  -支持每个数据库独立初始化表结构schema和数据库database。

  -支持无数据源启动,支持懒加载数据源(需要的时候再创建连接)。

  -支持**自定义注解**,需继承DS3.2.0+。

  -提供并简化对Druid,HikariCp,BeeCp,Dbcp2的快速集成。

  -提供对Mybatis-Plus,Quartz,ShardingJdbc,P6sy,Jndi等组件的集成方案。

  -提供**自定义数据源来源**方案(如全从数据库加载)。

  -提供项目启动后**动态增加移除数据源**方案。

  -提供Mybatis环境下的**纯读写分离**方案。

  -提供使用**spel动态参数**解析数据源方案。内置spel,session,header,支持自定义。

  -支持**多层数据源嵌套切换**。(ServiceA>>>ServiceB>>>ServiceC)。

  -提供**基于seata的分布式事务方案。

  -提供**本地多数据源事务方案。**
 

  4.2 约定
 

  1.本框架只做**切换数据源**这件核心的事情,并**不限制你的具体操作**,切换了数据源可以做任何CRUD。

  2.配置文件所有以下划线`_`分割的数据源**首部**即为组的名称,相同组名称的数据源会放在一个组下。

  3.切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。

  4.默认的数据源名称为**master**,你可以通过`spring.datasource.dynamic.primary`修改。

  5.方法上的注解优先于类上注解。

  6.DS支持继承抽象类上的DS,暂不支持继承接口上的DS。
 

  4.3 使用
 

  4.3.1准备数据库

 docker run --name mysq -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7

  创建2个数据库

create database test1;
create database test2;


use test2;

-- auto-generated definition
create table tb_user
(
    id   int auto_increment
        primary key,
    name varchar(200) null
);

insert into tb_user values(1,"wangwu");


use test1;

-- auto-generated definition
create table tb_user
(
    id   int auto_increment
        primary key,
    name varchar(200) null
);
insert into tb_user values(1,"zhangsan");

  一个作为主库一个作为从库。
 

  4.3.2 springboot创建工程
 

  添加依赖:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>dynamic</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>

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

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
    </dependencies>
</project>

  启动类:

package com.itheima;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DynamicApplication 
    public static void main(String[] args) 
        SpringApplication.run(DynamicApplication.class,args);
    

  4.3.3配置yml

spring:
  datasource:
    dynamic:
      primary: master #设置默认的数据源或者数据源组,默认值即为master
      strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
      datasource:
        master:
          url: jdbc:mysql://192.168.211.253:3306/test1
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
        slave_1:
          url: jdbc:mysql://192.168.211.253:3306/test2
          username: root
          password: 123456
          driver-class-name: com.mysql.jdbc.Driver
        #......省略
        #以上会配置一个默认库master,一个组slave下有两个子库slave_1

  4.3.4使用注解来切换数据源
 

  使用**DS**切换数据源,使用方式如下:
 

  **DS**可以注解在方法上或类上,**同时存在就近原则方法上注解优先于类上注解**。

  |注解|结果|

  |-------------|----------------------------------------|

  |没有DS|默认数据源|

  |DS

"dsName"

|dsName可以为组名也可以为具体某个库的名称|
 

  例如:

@Service
@DS("slave")
public class UserServiceImpl implements UserService 

  @Autowired
  private JdbcTemplate jdbcTemplate;

  public List selectAll() 
    return  jdbcTemplate.queryForList("select * from user");
  
 
  @Override
  @DS("slave_1")
  public List selectByCondition() 
    return  jdbcTemplate.queryForList("select * from user where age >10");
  

  4.3.5创建CSD

  po:

package com.itheima.po;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@Data
@TableName("tb_user")
public class User 
    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField("name")
    private String name;

  controller

package com.itheima.controller;

import com.itheima.po.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
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;

@RestController
@RequestMapping("/user")
public class UserController 
    @Autowired
    private UserService userService;

    @GetMapping("/id")
    public User get(@PathVariable(name="id")Integer id)
        return userService.getById(id);
    

  service

  ```

  public interface UserService

  User getById

Integer id

;

  

  ```

package com.itheima.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.itheima.dao.UserDao;
import com.itheima.po.User;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
@DS("slave_1")
public class UserServiceImpl implements UserService 
    @Autowired
    private UserDao userDao;

    @Override
    public User getById(Integer id) 
        return userDao.selectById(id);
    

  注意:如上:DS注解用于指定使用哪一个数据源。
 

  dao

package com.itheima.dao;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.po.User;

public interface UserDao extends BaseMapper<User> 

  启动类:

@SpringBootApplication
@MapperScan(basePackages = "com.itheima.dao")
public class DynamicApplication 
    public static void main(String[] args) 
        SpringApplication.run(DynamicApplication.class,args);
    

  4.3.6测试
 

  +当使用DS注解指定master的时候:

  ```

  Service

  DS

"master"

  public class UserServiceImpl implements UserService

  Autowired

  private UserDao userDao;

  Override

  public User getById

Integer id

  return userDao.selectById

id

;

  

  

  ```

  重启项目,浏览器发送请求:http://localhost:8080/user/1
 

  得到结果:

  ```

  "id":1,"name":"zhangsan"

  ```

  +当使用DS注解指定slave_1的时候

  ```

  Service

  DS

"slave_1"

  public class UserServiceImpl implements UserService

  Autowired

  private UserDao userDao;

  Override

  public User getById

Integer id

  return userDao.selectById

id

;

  

  

  ```

  重启项目,浏览器发送请求:http://localhost:8080/user/1
 

  得到结果:

"id":1,"name":"wangwu"

  测试成功。
 

  5.总结
 

  使用mybatisplus的动态数据源切换非常方便,只需添加依赖,并在yaml中配置数据源的名称和地址,并在service的实现类中使用注解来指定实现切换即可。下一章节我们来看看如何使用AOP来实现不需要修改代码就能动态切换数据源。
 

  +添加依赖

  +添加yaml配置

  +在业务方法上或者业务类上添加 DS注解

SpringBoot整合MyBatisPlus配置动态数据源

参考技术A

MybatisPlus特性

快速开始

初始化测试数据表:

父工程依赖

该工程用于依赖管理,pom如下:

创建MyBaitsPlus工程

依赖如下:

properties配置

在这里配置数据库连接,以及数据连接池与mybatisplus的配置等

常规增删改查实现

创建实体类:

该lombok插件省去getset方法。

创建UserMapper接口,并且实现BaseMapper<User>这里我们指定实体类为user可直接使用接口中的方法。

UserMapper.xml,如下:

执行SQL:

在SpringBoot中使用MybatisPlus分页需要注入Bean,并且在启动类上使用@MapperScan("com.simple.spring.boot.mapper")扫描mapper文件路径如下:

使用MyBatisPlus可以为我们减少很多很多的代码,不过需要编写实体类,有失必有得。

配置动态数据源

dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。

优势

网上关于动态数据源的切换的文档有很多,核心只有两种。

如果你的数据源较少,场景不复杂,选择以上任意一种都可以。如果你需要更多特性,请尝试本动态数据源。

劣势

不能使用多数据源事务(同一个数据源下能使用事务),网上其他方案也都不能提供。

如果你需要使用到分布式事务,那么你的架构应该到了微服务化的时候了。

如果呼声强烈,项目达到800 star,作者考虑集成分布式事务。

PS: 如果您只是几个数据库但是有强烈的需求分布式事务,建议还是使用传统方式自己构建多套环境集成atomic这类,网上百度很多。

约定

建议

强烈建议在 主从模式 下遵循普遍的规则,以便他人能更轻易理解你的代码。

主数据库 建议 只执行 INSERT UPDATE DELETE 操作。

从数据库 建议 只执行 SELECT 操作。

快速开始

加入依赖:

注释掉原来的数据库配置,加入:

使用 @DS 切换数据源。

@DS 可以注解在方法上和类上, 同时存在方法注解优先于类上注解

注解在service实现或mapper接口方法上,但强烈不建议同时在service和mapper注解。 (可能会有问题)

如果不加入主键则使用默认数据源。

DruidDataSourceAutoConfigure会注入一个DataSourceWrapper,其会在原生的spring.datasource下找url,username,password等。而我们动态数据源的配置路径是变化的,所以需要排除:

或者在类上排除:

然后更换properties配置信息:

本篇代码案例地址:

https://github.com/450255266/open-doubi

作者:SimpleWu

出处:https://www.cnblogs.com/SimpleWu/p/10930388.html

以上是关于mybatisplus快速实现动态数据源切换的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot集成Mybatis动态多数据源后,MybatisPlus的IPage失效的问题解决方案

mybatis plus自定义的mapper如何动态切换数据源

基于mybatis的动态表数据增删改查方法实现

基于mybatis的动态表数据增删改查方法实现

小书MybatisPlus第3篇-自定义SQL

mybatis-plus 动态数据源读写分离 + shardingJDBC分库分表