springboot2.x整合mybatis-plus,mysql实现多数据源

怎么新建springboot项目我在这里就不介绍了,首先我们先来看一下需要引入哪些主要的jar

1:pom.xml



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.7.RELEASE
         
    
    com.example
    dome
    0.0.1-SNAPSHOT
    dome
    Demo project for Spring Boot

    
        UTF-8
        UTF-8
        1.8
        1.1.2
        8.0.7-dmr
        2.1.8
        1.0.5
        2.9.0
        1.2.45
        1.18.8
    

    
        
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            org.springframework.boot
            spring-boot-starter-aop
        

        
        
            mysql
            mysql-connector-java
            ${mysql-connector.version}
            runtime
        
        
            com.alibaba
            druid
            ${druid.version}
        
        


        
        
            com.baomidou
            mybatis-plus
            ${mybatis-plus.version}
        
        
            com.baomidou
            mybatisplus-spring-boot-starter
            ${mybatisplus-spring-boot-starter.version}
        
        
        
            com.github.pagehelper
            pagehelper-spring-boot-starter
            1.2.5
        
        


        
        
            com.baomidou
            mybatis-plus-generator
            3.0.7.1
        

        
        
            org.apache.velocity
            velocity-engine-core
            2.0
        

        
        
            org.freemarker
            freemarker
            2.3.23
        
        
        
            com.ibeetl
            beetl
            2.2.5
        
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
            org.hamcrest
            hamcrest-all
            1.3
            test
        

        
            org.springframework.boot
            spring-boot-configuration-processor
            true
        

        
        
            org.projectlombok
            lombok
            ${lombok.version}
            provided
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            redis.clients
            jedis
            ${redis-clien.version}
        
        

        
        
            com.alibaba
            fastjson
            ${fast-json.version}
        

    

    
        
        compile
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.6.1
                
                    
                    1.8
                    1.8
                
            
            
                org.apache.maven.plugins
                maven-surefire-plugin
                2.20
                
                    true
                
            
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                
            
        
        
            
                src/main/resources
            
            
                src/main/java
                
                    **/*.xml
                
            
        
    


2:application.yml

#自定义配置
example:
  muti-datasource-open: true #是否开启多数据源(true/false)
  redis-multi-open: false

#spring配置
spring:
  profiles:
    active: dev
#端口
server:
  port: 8987
  
---
#主数据源
spring:
  profiles: dev
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/expo0917?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
    username: root
    password: root

#数据源 biz
biz:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/xm001dev?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8
    username: root
    password: root

logging:
  level:
    ROOT: INFO
    com:
      example: DEBUG
  file: D:/logs/app.log
  
---
#mybatis-plus配置
mybatis-plus:
  mapper-locations: classpath*:com/example/dome/common/dao/repository/mapping/*.xml
  typeAliasesPackage: >
    com.example.dome.common.dao.entity
  global-config:
    id-type: 0  # 0:数据库ID自增   1:用户输入id  2:全局唯一id(IdWorker)  3:全局唯一ID(uuid)
    db-column-underline: false
    refresh-mapper: true
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true #配置的缓存的全局开关
    lazyLoadingEnabled: true #延时加载的开关
    multipleResultSetsEnabled: true #开启的话,延时加载一个属性时会加载该对象全部属性,否则按需加载属性

logging:
  level:
    org.springframework.web.servlet: ERROR

---
#Redis
spring:
  redis:
    host: 192.168.11.200
    porl: 6379
    database: 0
    password:
    timeout: 6000ms
    jedis:
      pool:
        max-active: 1000    # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms    # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 10      # 连接池中的最大空闲连接
        min-idle: 5       # 连接池中的最小空闲连接
  cache:
    type: none

关于yml里面的自定义配置内容提示可以参考:https://blog.csdn.net/qq_34805041/article/details/100103980

 

3:Druid数据连接池配置

package com.example.dome.config.properties;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.util.JdbcConstants;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.sql.SQLException;

/**
 * @Time 2019/8/28 18:54
 * @Author GuiPing.Xu
 * @Version 1.0.0
 * @Explain  

数据库数据源配置

*

说明:这个类中包含了许多默认配置,若这些配置符合您的情况,您可以不用管,若不符合,建议不要修改本类,建议直接在"application.yml"中配置即可

*/ @Component @ConfigurationProperties(prefix = "spring.datasource") public class DruidProperties { private String url; private String username; private String password; private String driverClassName = "com.mysql.cj.jdbc.Driver"; private Integer initialSize = 10; private Integer minIdle = 3; private Integer maxActive = 60; private Integer maxWait = 60000; private Boolean removeAbandoned = true; private Integer removeAbandonedTimeout = 180; private Integer timeBetweenEvictionRunsMillis = 60000; private Integer minEvictableIdleTimeMillis = 300000; private String validationQuery = "SELECT 'x'"; private Boolean testWhileIdle = true; private Boolean testOnBorrow = false; private Boolean testOnReturn = false; private Boolean poolPreparedStatements = true; private Integer maxPoolPreparedStatementPerConnectionSize = 50; private String filters = "stat"; public void config(DruidDataSource dataSource) { dataSource.setDbType(JdbcConstants.MYSQL); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); // 定义初始连接数 dataSource.setInitialSize(initialSize); // 最小空闲 dataSource.setMinIdle(minIdle); // 定义最大连接数 dataSource.setMaxActive(maxActive); // 获取连接等待超时的时间 dataSource.setMaxWait(maxWait); // 超过时间限制是否回收 dataSource.setRemoveAbandoned(removeAbandoned); // 超过时间限制多长 dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout); // 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 配置一个连接在池中最小生存的时间,单位是毫秒 dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 用来检测连接是否有效的sql,要求是一个查询语句 dataSource.setValidationQuery(validationQuery); // 申请连接的时候检测 dataSource.setTestWhileIdle(testWhileIdle); // 申请连接时执行validationQuery检测连接是否有效,配置为true会降低性能 dataSource.setTestOnBorrow(testOnBorrow); // 归还连接时执行validationQuery检测连接是否有效,配置为true会降低性能 dataSource.setTestOnReturn(testOnReturn); // 打开PSCache,并且指定每个连接上PSCache的大小 dataSource.setPoolPreparedStatements(poolPreparedStatements); dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); // 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: // 监控统计用的filter:stat // 日志用的filter:log4j // 防御SQL注入的filter:wall try { dataSource.setFilters(filters); } catch (SQLException e) { e.printStackTrace(); } } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getDriverClassName() { return driverClassName; } public void setDriverClassName(String driverClassName) { this.driverClassName = driverClassName; } public Integer getInitialSize() { return initialSize; } public void setInitialSize(Integer initialSize) { this.initialSize = initialSize; } public Integer getMinIdle() { return minIdle; } public void setMinIdle(Integer minIdle) { this.minIdle = minIdle; } public Integer getMaxActive() { return maxActive; } public void setMaxActive(Integer maxActive) { this.maxActive = maxActive; } public Integer getMaxWait() { return maxWait; } public void setMaxWait(Integer maxWait) { this.maxWait = maxWait; } public Integer getTimeBetweenEvictionRunsMillis() { return timeBetweenEvictionRunsMillis; } public void setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis) { this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; } public Integer getMinEvictableIdleTimeMillis() { return minEvictableIdleTimeMillis; } public void setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis) { this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; } public String getValidationQuery() { return validationQuery; } public void setValidationQuery(String validationQuery) { this.validationQuery = validationQuery; } public Boolean getTestWhileIdle() { return testWhileIdle; } public void setTestWhileIdle(Boolean testWhileIdle) { this.testWhileIdle = testWhileIdle; } public Boolean getTestOnBorrow() { return testOnBorrow; } public void setTestOnBorrow(Boolean testOnBorrow) { this.testOnBorrow = testOnBorrow; } public Boolean getTestOnReturn() { return testOnReturn; } public void setTestOnReturn(Boolean testOnReturn) { this.testOnReturn = testOnReturn; } public Boolean getPoolPreparedStatements() { return poolPreparedStatements; } public void setPoolPreparedStatements(Boolean poolPreparedStatements) { this.poolPreparedStatements = poolPreparedStatements; } public Integer getMaxPoolPreparedStatementPerConnectionSize() { return maxPoolPreparedStatementPerConnectionSize; } public void setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize) { this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize; } public String getFilters() { return filters; } public void setFilters(String filters) { this.filters = filters; } public Boolean getRemoveAbandoned() { return removeAbandoned; } public void setRemoveAbandoned(Boolean removeAbandoned) { this.removeAbandoned = removeAbandoned; } public Integer getRemoveAbandonedTimeout() { return removeAbandonedTimeout; } public void setRemoveAbandonedTimeout(Integer removeAbandonedTimeout) { this.removeAbandonedTimeout = removeAbandonedTimeout; } }

4:自定义注解类,用来使用在service方法上指定改方法要使用的数据源

package com.example.dome.common.annotion;

import java.lang.annotation.*;

/**
 *  @Time 2019/8/27 18:40
 *  @Author GuiPing.Xu
 *  @Version 1.0.0
 *  @Explain 自定义注解类
 *
 * java 四大元注解作用
 * 1.@Target :用于描述注解的使用范围,也就是说使用了@Target去定义一个注解,
 *             那么可以决定定义好的注解能用在什么地方
 *             1.CONSTRUCTOR:用于描述构造器
 *          2.FIELD:用于描述域
 *          3.LOCAL_VARIABLE:用于描述局部变量
 *          4.METHOD:用于描述方法
 *          5.PACKAGE:用于描述包
 *          6.PARAMETER:用于描述参数
 *          7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
 * 2.@Retention:用于描述注解的生命周期,也就是说这个注解在什么范围内有效,
 *               注解的生命周期和三个阶段有关:源代码阶段、CLASS文件中有效、运行时有效,
 *               故其取值也就三个值,分别代表着三个阶段
 *               1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
 *               2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
 *               3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
 * 3.@Documented:表示该注解是否可以生成到 API文档中。在该注解使用后,如果导出API文档,
 *                会将该注解相关的信息可以被例如javadoc此类的工具文档化。
 *                注意:Documented是一个标记注解,没有成员。
 * 4.@Inherited:使用@Inherited定义的注解具备继承性
 *              假设一个注解在定义时,使用了@Inherited,然后该注解在一个类上使用,如果这个类有子类,
 *              那么通过反射我们可以从类的子类上获取到同样的注解、
 */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
    String name() default "";
}

5:定义多数据源枚举常量类

package com.example.dome.common.mutidatesource;

/**
 * @Time 2019/8/27 18:40
 * @Author GuiPing.Xu
 * @Version 1.0.0
 * @Explain 多数据源的常量类
 */
public interface DSEnum {
    /**
     * 核心数据库
     */
    String DATA_SOURCE_CORE="dataSourceCore";

    /**
     * 其他业务数据库
     */
    String DATA_SOURCE_BIZ="dataSourceBiz";

}

6:配置DataSource上下文,用来存储当前线程的数据源

package com.example.dome.common.mutidatesource;

/**
 * @Time 2019/8/27 18:45
 * @Author GuiPing.Xu
 * @Version 1.0.0
 * @Explain dataSource的上下文 用来存储当前线程的数据源类型
 */
public class DataSourceContextHolder {
    private static final  ThreadLocal contextHolder=new ThreadLocal();

    /**
     * @param dataSourceType 数据库类型
     * @Description: 设置数据源类型
     */
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    /**
     * @Description: 获取数据源类型
     */
    public static String getDataSourceType() {
        return contextHolder.get();
    }

    /**
     * @Description: 清除数据源类型
     */
    public static void clearDataSourceType() {
        contextHolder.remove();
    }

}

7:定义动态数据源

package com.example.dome.common.mutidatesource;


/**
 * @Time 2019/8/27 18:48
 * @Author GuiPing.Xu
 * @Version 1.0.0
 * @Explain 动态数据源
 */
public class AbstractRoutingDataSource extends org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

8:最核心最重要的AOP类

package com.example.dome.common.aop;

import com.example.dome.common.annotion.DataSource;
import com.example.dome.common.mutidatesource.DSEnum;
import com.example.dome.common.mutidatesource.DataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Time 2019/8/28 11:43
 * @Author GuiPing.Xu
 * @Version 1.0.0
 * @Explain AOP切面类
 *
 */
@Aspect
@Component
@ConditionalOnProperty(prefix = "example",name = "muti-datasource-open",havingValue = "true")
public class    MultiSourceExAop implements Ordered {

    Logger logger= LoggerFactory.getLogger(this.getClass());

    /**
     * 定义一个Annotation 类型的切点,当spring 扫描到自定义注解@DataSource,进入该切点
     */
    @Pointcut(value = "@annotation(com.example.dome.common.annotion.DataSource)")
    private void cut() {

    }

    /**
     * 可以同时在所拦截cut()方法的前后执行一段逻辑
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("cut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        Signature signature = point.getSignature();
        MethodSignature methodSignature = null;
        if (!(signature instanceof MethodSignature)) {
            throw new IllegalArgumentException("该注解只能用于方法");
        }
        methodSignature = (MethodSignature) signature;

        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());

        DataSource datasource = currentMethod.getAnnotation(DataSource.class);
        if (datasource != null) {
            DataSourceContextHolder.setDataSourceType(datasource.name());
            logger.debug("设置数据源为:" + datasource.name());
        } else {
            DataSourceContextHolder.setDataSourceType(DSEnum.DATA_SOURCE_CORE);
            logger.debug("设置数据源为:dataSourceCore");
        }
        try {
            return point.proceed();
        } finally {
            logger.debug("清空数据源信息!");
            DataSourceContextHolder.clearDataSourceType();
        }
    }

    /**
     * AOP事务顺序  AOP的顺序要早于spring的事务
     * @return
     */ 
    @Override
    public int getOrder() {
        return 1;
    }
}

配置信息类

package com.example.dome.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.example.dome.common.mutidatesource.AbstractRoutingDataSource;
import com.example.dome.common.mutidatesource.DSEnum;
import com.example.dome.config.properties.DruidProperties;
import com.example.dome.config.properties.MutiDataSourceProperties;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.sql.SQLException;
import java.util.HashMap;

/**
 * @Time 2019/8/28 15:33
 * @Author GuiPing.Xu
 * @Version 1.0.0
 * @Explain
 */
@Configuration
@EnableTransactionManagement(order = 2)
@MapperScan(basePackages = {"com.example.dome.common.mapper"})
public class MybatisPlusConfig {
    @Autowired
    DruidProperties druidProperties;
    @Autowired
    MutiDataSourceProperties mutiDataSourceProperties;

    /**
     * 核心数据源
     */
    private DruidDataSource coreDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        druidProperties.config(dataSource);
        return dataSource;
    }

    /**
     * 另一个数据源
     */
    private DruidDataSource bizDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        druidProperties.config(dataSource);
        mutiDataSourceProperties.config(dataSource);
        return dataSource;
    }

    /**
     * 单数据源连接池配置  通过@ConditionalOnProperty来控制Configuration是否生效
     */
    @Bean
    @ConditionalOnProperty(prefix = "example", name = "muti-datasource-open", havingValue = "false")
    public DruidDataSource singleDatasource() {
        return coreDataSource();
    }

    /**
     * 多数据源连接池配置
     */
    @Bean
    @ConditionalOnProperty(prefix = "example", name = "muti-datasource-open", havingValue = "true")
    public AbstractRoutingDataSource mutiDataSource() {

        DruidDataSource coreDataSource = coreDataSource();
        DruidDataSource bizDataSource = bizDataSource();
        try {
            coreDataSource.init();
            bizDataSource.init();
        } catch (SQLException sql) {
            sql.printStackTrace();
        }

        AbstractRoutingDataSource dynamicDataSource = new AbstractRoutingDataSource();
        HashMap hashMap = new HashMap<>();
        hashMap.put(DSEnum.DATA_SOURCE_CORE, coreDataSource);
        hashMap.put(DSEnum.DATA_SOURCE_BIZ, bizDataSource);
        dynamicDataSource.setTargetDataSources(hashMap);
        dynamicDataSource.setDefaultTargetDataSource(coreDataSource);
        return dynamicDataSource;
    }
}

GitHub源代码地址:https://github.com/xuguipinga/springBootStudy/tree/master/multipleDataSources

本人也是第一次写,其中有一些代码也是借鉴一些大牛的,如有什么不合理的地方,希望可以指出来,多多交流,学习

你可能感兴趣的:(springboot,Mysql,多数据源)