• springboot+mybatis plus配置多数据源


    最近配置多数据源,也是bug频出,在参考了诸多文档,掉了些许头发之后,现在测试OK了,特此分享。本次采用注解的方式,通过AOP来切换不同数据源,也可以通过拦截方法来切换数据源。

    !注意点:包的导入和注解的标注,避免jar冲突。

    相关版本:jdk1.8,springboot 2.1.3

    1》pom.xml导包

            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.1.0</version>
            </dependency>       
             <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-extension</artifactId>
                <version>3.1.0</version>
            </dependency>     
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>    
             <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.9</version>
            </dependency>    
             <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jdbc</artifactId>
            </dependency>    

    2》yml配置

    spring:
      datasource:
             druid:
                db1:
                   username: root
                   password: 123456
                   driverClassName: com.mysql.jdbc.Driver
                   url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
                db2:
                   username: root
                   password: 123456
                   driverClassName: com.mysql.jdbc.Driver
                   url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
                db3:
                   username: root
                   password: 123456
                   driverClassName: com.mysql.jdbc.Driver
                   url: jdbc:mysql://localhost:3306/db3?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai

    3》数据源相关设置

    1、DatasourceConstants类:

    /**
     * 数据源常量
     */
    public class DatasourceConstants {
    
            public static final String DATASOURCE1 = "datasource1";
            public static final String DATASOURCE2 = "datasource2";
            public static final String DATASOURCE3 = "datasource3";
    }

    2、DataSourceContextHolder类:

    /**
     * 数据源容容器
     *
     * 
     */
    public class DataSourceContextHolder {
    
        public static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
    
        /**
         * 获取数据源名
         * @return
         */
        public static String getDataSource(){
            return (String) HOLDER.get();
        }
    
        /**
         * 设置数据源名
         * @param dataSourceName
         */
        public static void setDataSource(String dataSourceName){
            HOLDER.set(dataSourceName);
        }
    
        /**
         * 移除数据源名
         */
        public  static void removeDataSource(){
            HOLDER.remove();
        }
    }

    3、DynamicDataSource类,这是很关键的一步,继承自AbstractRoutingDataSource抽象类,里面有个重要的方法:部分源码

    /**
         * Retrieve the current target DataSource. Determines the
         * {@link #determineCurrentLookupKey() current lookup key}, performs
         * a lookup in the {@link #setTargetDataSources targetDataSources} map,
         * falls back to the specified
         * {@link #setDefaultTargetDataSource default target DataSource} if necessary.
         * @see #determineCurrentLookupKey()
         */
        protected DataSource determineTargetDataSource() {
            Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
            Object lookupKey = determineCurrentLookupKey();
            DataSource dataSource = this.resolvedDataSources.get(lookupKey);
            if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
                dataSource = this.resolvedDefaultDataSource;
            }
            if (dataSource == null) {
                throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
            }
            return dataSource;
        }

    我们需要重写determineCurrentLookupKey()方法:

    
    
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    /**
     * 获取动态数据源
     * 
     * 
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceContextHolder.getDataSource();
        }
    }

    4、自定义MyDataSource 注解:

    /**
     * 数据源注解
     * @author yangxiaoguang
     * @date 2020/5/14
     */
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    public @interface MyDataSource {
        String value() default "";
    }

    5、DynamicDataSourceAspect切面类:

    import org.aspectj.lang.JoinPoint;
    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.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    /**
     * 数据源切面  需要在事务@Transactional之前运行
     * 
     * 
     */
    @Component
    @Aspect
    @Order(-1)
    public class DynamicDataSourceAspect {
    
        //切点表达式
        @Pointcut("@annotation(com.springboot.dataSource.MyDataSource)")
        public void  pointcut(){
    
        }
    
        @Before("pointcut()")
        public void  beforeMethod(JoinPoint joinPoint) throws NoSuchMethodException {
            //获取class对象
            Class<?> clazz = joinPoint.getTarget().getClass();
            //获取方法名
            String methodName = joinPoint.getSignature().getName();
            //获取方法参数类型
            Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
            //获取到Method类
            Method method = clazz.getMethod(methodName, parameterTypes);
            //获取到注解
            MyDataSource annotation = method.getAnnotation(MyDataSource.class);
            //获取注解上的值
            String dataSourceName = annotation.value();
            //设置数据源
            DataSourceContextHolder.setDataSource(dataSourceName);
        }
    
        @After("pointcut()")
        public void afterSwitchDS(JoinPoint joinPoint) {
            DataSourceContextHolder.removeDataSource();
        }
    }

    4》MybatisPlusConfig 配置文件:

    import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
    import com.baomidou.mybatisplus.core.MybatisConfiguration;
    import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
    
    import com.springboot.dataSource.DatasourceConstants;
    import com.springboot.dataSource.DynamicDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.type.JdbcType;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    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.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    
    @EnableTransactionManagement
    @Configuration
    @MapperScan("com.springboot.modules.*.mapper")
    public class MybatisPlusConfig {
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.druid.db1")
        public DataSource db1() {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.druid.db2")
        public DataSource db2() {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.druid.db3")
        public DataSource db3() {
            return DruidDataSourceBuilder.create().build();
        }
    
        /**
         * 动态数据源配置
         * @return
         */
        @Bean
        @Primary
        public DataSource setDynamicDataSource( DataSource db1,  DataSource db2, DataSource db3) {
    
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            //设置默认的数据源,不设置会报错
            dynamicDataSource.setDefaultTargetDataSource(db1);
    
            //配置多数据源,加入其他数据源到map中v
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DatasourceConstants.DATASOURCE1, db1);
            targetDataSources.put(DatasourceConstants.DATASOURCE2, db2);
            targetDataSources.put(DatasourceConstants.DATASOURCE3, db3);
            dynamicDataSource.setTargetDataSources(targetDataSources);
    
            return dynamicDataSource;
        }
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
    
            MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
            sqlSessionFactory.setDataSource(setDynamicDataSource(db1(), db2(),db3()));
    
            //数据库相关设置
            MybatisConfiguration configuration = new MybatisConfiguration();
            configuration.setJdbcTypeForNull(JdbcType.NULL);
            configuration.setMapUnderscoreToCamelCase(true);
            configuration.setCacheEnabled(false);
            sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));
            sqlSessionFactory.setConfiguration(configuration);
            return sqlSessionFactory.getObject();
        }
    }

    5》测试:

    controller层:这里整合swagger ui

       @ApiOperation(value = "查询个人学生信息")
        @PostMapping(value = "/findStudent")
        public ResponseData findStudent(@RequestBody StudentReq studentReq){
            return ResponseData.okData(studentService.findStudent(studentReq));
        }

    service层:

       @Override
        @MyDataSource("datasource2")
        public Student findStudent(StudentReq studentReq) {
            return studentMapper.selectOne(new QueryWrapper<>());
        }

    数据库:

    db1:student表

    db2:student表

    db3:student表

    测试结果:

    默认数据源db1,切换数据源db2:

     默认数据源db1,切换数据源db3:

     以上就是多数据源的基本配置了,有啥不当可以交流交流。

    参考文档:

    https://mp.baomidou.com/guide/dynamic-datasource.html

    https://yq.aliyun.com/articles/634622

  • 相关阅读:
    学习Spring.Net:1.简单的应用之控制台
    学习Memcached:2基本应用之控制台使用
    学习Memcached:1基本配置与安装
    C#IAsyncResult异步回调函数的解释
    C# JSON格式数据高级用法
    C#CRC16 Modbus 效验算法
    第一篇编程笔记
    SqlParameter 操作 image 字段
    C# 对象复制
    利用触发器实现数据同步
  • 原文地址:https://www.cnblogs.com/tdyang/p/12895545.html
Copyright © 2020-2023  润新知