首頁技術(shù)文章正文

Java培訓(xùn):mybatisplus快速實現(xiàn)動態(tài)數(shù)據(jù)源切換

更新時間:2022-11-21 來源:黑馬程序員 瀏覽量:

      1.背景

  通常一個系統(tǒng)只需要連接一個數(shù)據(jù)庫就可以了。但是在企業(yè)應(yīng)用的開發(fā)中往往會和其他子系統(tǒng)交互,特別是對于一些數(shù)據(jù)實時性要求比較高的數(shù)據(jù),我們就需要做實時連接查詢,而不是做同步。這個時候就需要用到多數(shù)據(jù)源。

  舉個簡單的例子某企業(yè)要做訂單網(wǎng)上訂單系統(tǒng)這里面就可以涉及到多個子系統(tǒng)的連接,比如:產(chǎn)品主數(shù)據(jù)的數(shù)據(jù)源,項目管理系統(tǒng)的數(shù)據(jù)源(項目可以產(chǎn)品訂單)等多個不同數(shù)據(jù)庫類似的數(shù)據(jù)源,他們可能是ORACLE,SQL SERVER,MYSQL等多種混合數(shù)據(jù)源。

  2.多數(shù)據(jù)源概述

  基于以上的背景,就會選擇使用多個數(shù)據(jù)源,一個數(shù)據(jù)源用于讀一個數(shù)據(jù)源用于寫。或者不同的數(shù)據(jù)源混合使用。他的基本思想其實就是AOP。我們可以通過AOP的思想實現(xiàn)動態(tài)數(shù)據(jù)源切換,通過這個AOP思想可適用于多種場景、純粹多庫、讀寫分離、一主多從、混合模式等。

  動態(tài)數(shù)據(jù)源能進(jìn)行自動切換的核心就是spring底層的AbstractRoutingDataSource進(jìn)行數(shù)據(jù)源的路由,只要繼承了這個類均可看作是一個數(shù)據(jù)源的實現(xiàn)。主要實現(xiàn)方法是determineCurrentLookupkey,該方法只需要返回數(shù)據(jù)源實例名稱。

  3.mybatisplus多數(shù)據(jù)源

  我們在項目中用mybatisplus的使用用得比較多,這個動態(tài)數(shù)據(jù)源切換需要實現(xiàn)的話,比較麻煩,如果有現(xiàn)成的框架使用則最好不過了。恰好mybatiplus就能實現(xiàn)。文檔地址如下:

  ```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的快速集成多數(shù)據(jù)源的啟動器。

  特性:

  -支持**數(shù)據(jù)源分組**,適用于多種場景純粹多庫讀寫分離一主多從混合模式。

  -支持?jǐn)?shù)據(jù)庫敏感配置信息**加密**ENC。

  -支持每個數(shù)據(jù)庫獨立初始化表結(jié)構(gòu)schema和數(shù)據(jù)庫database。

  -支持無數(shù)據(jù)源啟動,支持懶加載數(shù)據(jù)源(需要的時候再創(chuàng)建連接)。

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

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

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

  -提供**自定義數(shù)據(jù)源來源**方案(如全從數(shù)據(jù)庫加載)。

  -提供項目啟動后**動態(tài)增加移除數(shù)據(jù)源**方案。

  -提供Mybatis環(huán)境下的**純讀寫分離**方案。

  -提供使用**spel動態(tài)參數(shù)**解析數(shù)據(jù)源方案。內(nèi)置spel,session,header,支持自定義。

  -支持**多層數(shù)據(jù)源嵌套切換**。(ServiceA>>>ServiceB>>>ServiceC)。

  -提供**基于seata的分布式事務(wù)方案。

  -提供**本地多數(shù)據(jù)源事務(wù)方案。**

  4.2 約定

  1.本框架只做**切換數(shù)據(jù)源**這件核心的事情,并**不限制你的具體操作**,切換了數(shù)據(jù)源可以做任何CRUD。

  2.配置文件所有以下劃線`_`分割的數(shù)據(jù)源**首部**即為組的名稱,相同組名稱的數(shù)據(jù)源會放在一個組下。

  3.切換數(shù)據(jù)源可以是組名,也可以是具體數(shù)據(jù)源名稱。組名則切換時采用負(fù)載均衡算法切換。

  4.默認(rèn)的數(shù)據(jù)源名稱為**master**,你可以通過`spring.datasource.dynamic.primary`修改。

  5.方法上的注解優(yōu)先于類上注解。

  6.DS支持繼承抽象類上的DS,暫不支持繼承接口上的DS。

  4.3 使用

  4.3.1準(zhǔn)備數(shù)據(jù)庫

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

  創(chuàng)建2個數(shù)據(jù)庫

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創(chuàng)建工程

  添加依賴:

<?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 #設(shè)置默認(rèn)的數(shù)據(jù)源或者數(shù)據(jù)源組,默認(rèn)值即為master
      strict: false #嚴(yán)格匹配數(shù)據(jù)源,默認(rèn)false. true未匹配到指定數(shù)據(jù)源時拋異常,false使用默認(rèn)數(shù)據(jù)源
      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
        #......省略
        #以上會配置一個默認(rèn)庫master,一個組slave下有兩個子庫slave_1

  4.3.4使用注解來切換數(shù)據(jù)源

  使用**DS**切換數(shù)據(jù)源,使用方式如下:

  **DS**可以注解在方法上或類上,**同時存在就近原則方法上注解優(yōu)先于類上注解**。

  |注解|結(jié)果|

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

  |沒有DS|默認(rèn)數(shù)據(jù)源|

  |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創(chuàng)建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注解用于指定使用哪一個數(shù)據(jù)源。

  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測試

  +當(dāng)使用DS注解指定master的時候:

  ```

  Service

  DS

"master"

  public class UserServiceImpl implements UserService{

  Autowired

  private UserDao userDao;

  Override

  public User getById

Integer id

{

  return userDao.selectById

id

;

  }

  }

  ```

  重啟項目,瀏覽器發(fā)送請求:http://localhost:8080/user/1

  得到結(jié)果:

  ```

  {"id":1,"name":"zhangsan"}

  ```

  +當(dāng)使用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

;

  }

  }

  ```

  重啟項目,瀏覽器發(fā)送請求:http://localhost:8080/user/1

  得到結(jié)果:

{"id":1,"name":"wangwu"}

  測試成功。

  5.總結(jié)

  使用mybatisplus的動態(tài)數(shù)據(jù)源切換非常方便,只需添加依賴,并在yaml中配置數(shù)據(jù)源的名稱和地址,并在service的實現(xiàn)類中使用注解來指定實現(xiàn)切換即可。下一章節(jié)我們來看看如何使用AOP來實現(xiàn)不需要修改代碼就能動態(tài)切換數(shù)據(jù)源。

  +添加依賴

  +添加yaml配置

  +在業(yè)務(wù)方法上或者業(yè)務(wù)類上添加 DS注解

分享到:
在線咨詢 我要報名
和我們在線交談!