SpringBoot2.x + MyBatis-Plus + Druid + 注解方式多数据源

【简介】

如果不熟悉MyBatis-Plus可以先看下这篇文章:SpringBoot2.x整合MyBatis-Plus

本文主要讲解使用注解的方式配置多数据源


【推荐文章】

SpringBoot+AOP构建多数据源的切换实践


【本文Demo】

https://github.com/qidasheng2012/springbooot2.x-mybatis-plus-datasources

本文只会把核心的一部分代码粘贴出来进行讲解,道友可以根据上面的GitHub地址clone下项目,修改为自己的数据库配置,项目即可跑的起来


【建表sql】

下图是项目提供的建表sql,建system和server两个数据库,分别执行下面的sql
SpringBoot2.x + MyBatis-Plus + Druid + 注解方式多数据源_第1张图片


【pom.xml】


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.2.0.RELEASEversion>
        <relativePath/> 
    parent>

    <groupId>com.seawaterbtgroupId>
    <artifactId>data-sourcesartifactId>
    <version>1.0.0version>
    <packaging>jarpackaging>
    <name>data-sourcesname>
    <description>SpringBoot2.x + MyBatis-plus + swagger2 + 多数据源description>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
        <mybatisPlus.version>3.2.0mybatisPlus.version>
        <druid.version>1.1.21druid.version>
        <swagger.version>2.9.2swagger.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-aopartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
            <version>${mybatisPlus.version}version>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>${druid.version}version>
        dependency>

        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>${swagger.version}version>
        dependency>
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger-uiartifactId>
            <version>${swagger.version}version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>

【application.yml】

实际开发过程中会有多个环境,所以项目给了dev、uat、pro三个环境的配置文件,这里只展示dev环境的,请根据自己的数据库进行修改

server:
  servlet:
    context-path: /ssm

spring:
  profiles:
    active: dev

  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      # 设置访问druid监控页的账号和密码,默认没有
      stat-view-servlet:
        login-username: admin
        login-password: admin

      # 配置扩展插件,配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat,wall,log4j
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 合并多个DruidDataSource的监控数据
      use-global-data-source-stat: true

      # 连接池配置
      #连接池建立时创建的初始化连接数
      initial-size: 5
      #连接池中最大的活跃连接数
      max-active: 20
      #连接池中最小的活跃连接数
      min-idle: 5
      #获取连接时最大等待时间,单位毫秒
      max-wait: 60000
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements: true
      max-open-prepared-statements: 20

      #在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位,一般比minEvictableIdleTimeMillis小
      time-between-eviction-runs-millis: 60000
      #连接池中连接,在时间段内一直空闲,被逐出连接池的时间(1000*60*60),以毫秒为单位
      min-evictable-idle-time-millis: 300000

      # 连接有效性检测
      #是否在连接空闲一段时间后检测其可用性
      test-while-idle: true
      #是否在获得连接后检测其可用性
      test-on-borrow: false
      #是否在连接放回连接池后检测其可用性
      test-on-return: false
      #mysql中为 select 'x'
      #oracle中为 select 1 from dual
      validation-query: SELECT 'x'

mybatis-plus:
  # 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
  # 如果是放在resource目录 classpath:/mapper/*Mapper.xml
  mapper-locations: classpath:/mapper/*Mapper.xml
  # 别名包扫描,多个package用逗号或者分号分隔
  type-aliases-package: com.ssm.entity
  
  configuration:
    #配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
    map-underscore-to-camel-case: true
    cache-enabled: false
    #配置JdbcTypeForNull, oracle数据库必须配置
    jdbc-type-for-null: 'null'
    
  global-config:
    #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
    id-type: 0
    #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
    field-strategy: 2
    #驼峰下划线转换
    db-column-underline: true
    #mp2.3+ 全局表前缀 t_
    table-prefix: t_
    #刷新mapper 调试神器
    refresh-mapper: true
    #数据库大写下划线转换
    capital-mode: true
    #逻辑删除配置(下面3个配置)
    logic-delete-value: 4
    logic-not-delete-value: 0

【application-dev.yml】

定义了两个数据源配置

server:
  port: 81

spring:
  datasource:
    druid:
      db1:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/system?serverTimezone=Asia/Shanghai&characterEncoding=utf8
        username: root
        password: 123456

      db2:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/server?serverTimezone=Asia/Shanghai&characterEncoding=utf8
        username: root
        password: 123456

【DataSourceEnum】

定义数据源枚举

package com.ssm.enums;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum DataSourceEnum {

    DB1("db1"),
    DB2("db2");

    private String value;

}

【DataSource】

定义数据源注解

package com.ssm.annotation;

import com.ssm.enums.DataSourceEnum;

import java.lang.annotation.*;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    DataSourceEnum value() default DataSourceEnum.DB1;
}

【DataSourceAspect】

定义数据源切面

package com.ssm.aop;

import com.ssm.annotation.DataSource;
import com.ssm.multiple.MultipleDataSource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
@Order(-1)
public class DataSourceAspect {

    @Pointcut("@within(com.ssm.annotation.DataSource) || @annotation(com.ssm.annotation.DataSource)")
    public void pointCut() {

    }

    @Before("pointCut() && @annotation(dataSource)")
    public void doBefore(DataSource dataSource) {
        String ds = dataSource.value().getValue();
        MultipleDataSource.setDataSource(ds);
    }

    @After("pointCut()")
    public void doAfter() {
        MultipleDataSource.clear();
    }
}

【MultipleDataSource】

package com.ssm.multiple;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class MultipleDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();

    @Override
    protected Object determineCurrentLookupKey() {
        return this.getDataSource();
    }

    /**
     * 设置数据源
     *
     * @param db
     */
    public static void setDataSource(String db) {
        contextHolder.set(db);
    }

    /**
     * 取得当前数据源
     *
     * @return
     */
    public static String getDataSource() {
        return contextHolder.get();
    }

    /**
     * 清除上下文数据
     */
    public static void clear() {
        contextHolder.remove();
    }
}

【MyBatiesPlusConfiguration】

package com.ssm.config;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.ssm.enums.DataSourceEnum;
import com.ssm.multiple.MultipleDataSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.ssm.mapper")
public class MyBatiesPlusConfiguration {

    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db1")
    public DataSource db1() {
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.druid.db2")
    public DataSource db2() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 动态数据源配置
     *
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("db1") DataSource db1, @Qualifier("db2") DataSource db2) {
        MultipleDataSource multipleDataSource = new MultipleDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceEnum.DB1.getValue(), db1);
        targetDataSources.put(DataSourceEnum.DB2.getValue(), db2);
        //添加数据源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源
        multipleDataSource.setDefaultTargetDataSource(db1);
        return multipleDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
        //添加分页插件
        sqlSessionFactory.setPlugins(new Interceptor[]{paginationInterceptor()});
        return sqlSessionFactory.getObject();
    }

    /**
     * 分页插件,自动识别数据库类型
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        return paginationInterceptor;
    }

}

【注解的使用】

上面已经配置好注解和多数据源的相关配置了,下面开始介绍注解的使用,因为注解定义时定义的使用范围是 ElementType.METHOD,所以使用时在ServiceImpl的方法上加上注解即可

package com.ssm.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ssm.annotation.DataSource;
import com.ssm.entity.Teacher;
import com.ssm.enums.DataSourceEnum;
import com.ssm.mapper.TeacherMapper;
import com.ssm.service.TeacherService;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.List;

@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherMapper, Teacher> implements TeacherService {
    @Override
    @DataSource(DataSourceEnum.DB2)
    public boolean save(Teacher entity) {
        return super.save(entity);
    }

    @Override
    @DataSource(DataSourceEnum.DB2)
    public boolean removeById(Serializable id) {
        return super.removeById(id);
    }

    @Override
    @DataSource(DataSourceEnum.DB2)
    public boolean updateById(Teacher entity) {
        return super.updateById(entity);
    }

    @Override
    @DataSource(DataSourceEnum.DB2)
    public List<Teacher> list(Wrapper<Teacher> queryWrapper) {
        return super.list(queryWrapper);
    }

    @Override
    @DataSource(DataSourceEnum.DB2)
    public IPage<Teacher> page(IPage<Teacher> page, Wrapper<Teacher> queryWrapper) {
        return super.page(page, queryWrapper);
    }
}

其他的都是基本的ssm代码这里不再一一粘贴讲述了,道友们clone下项目一看就一目了然了


【测试】

访问:http://127.0.0.1:81/ssm/student/list
SpringBoot2.x + MyBatis-Plus + Druid + 注解方式多数据源_第2张图片

访问:http://127.0.0.1:81/ssm/teacher/page?current=0&size=2
SpringBoot2.x + MyBatis-Plus + Druid + 注解方式多数据源_第3张图片
OK!多数据源生效,大功告成

你可能感兴趣的:(SpringBoot)