zoukankan      html  css  js  c++  java
  • springboot+druid+mybatis plus的多数据源配置

    springboot+druid+mybatis plus的多数据源配置

    https://www.cnblogs.com/puretuo/p/11365359.html

    记得面试时候,有面试官会问道,你们多数据源是怎么实现的呀。.......,一阵蒙蔽中,然后说道我们之前项目中,没有用到多数据源。

    所幸,目前做得项目中有一个业务逻辑中,用到多个数据库数据情况,多数据源华丽上线。

    一. mybatis plus

          因为我们项目是springboot+mybatis plus,有些人一看,mybatis还知道对吧,mybatis plus是什么鬼,其实字面意思可以理解,就是对mybatis进行一些功能改造,一些封装升级,然后用起来特别方便。

         核心功能的升级主要是以下三点:

         支持通用的 CRUD、代码生成器与条件构造器。

          通用 CRUD:定义好 Mapper 接口后,只需要继承 BaseMapper<T> 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件
         条件构造器:通过 EntityWrapper<T> (实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的 SQL
         代码生成器:支持一系列的策略配置与全局配置,比 MyBatis 的代码生成更好用

    二.多数据源配置开始

        思路:

      1、yml中配置多个数据源信息

      2、通过AOP切换不同数据源

      3、配合mybatis plus使用

         

    1、yml配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    spring:
      aop:
          proxy-target-classtrue
          auto: true
      datasource:
        druid:
          db1:
            url: jdbc:mysql://localhost:3306/eboot
            username: root
            password: root
            driver-class-name: com.mysql.jdbc.Driver
            initialSize: 5
            minIdle: 5
            maxActive: 20
          db2:
            url: jdbc:oracle:thin:@192.168.136.222:ORCL
            username: sa
            password: sa123456
            driver-class-name: oracle.jdbc.OracleDriver
            initialSize: 5
            minIdle: 5
            maxActive: 20

      



    2、启动多个数据源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    @EnableTransactionManagement //开启事务
    @Configuration  //spring中常用到注解,与xml配置相对立。是两种加载bean方式
    @MapperScan("com.df.openapi.**.mapper.db*"// 扫描mapperdao的地址
    public class MybatisPlusConfig {
     
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    //        paginationInterceptor.setLocalPage(true); // 由于版本问题,有些类可能招不到这个方法,需要升级jar包
            return paginationInterceptor;
        }
     
        @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) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
            targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
            dynamicDataSource.setTargetDataSources(targetDataSources);
            dynamicDataSource.setDefaultTargetDataSource(db2); // 程序默认数据源,这个要根据程序调用数据源频次,经常把常调用的数据源作为默认
            return dynamicDataSource;
        }
     
        @Bean("sqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
            sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
     
            MybatisConfiguration configuration = new MybatisConfiguration();
            configuration.setJdbcTypeForNull(JdbcType.NULL);
            configuration.setMapUnderscoreToCamelCase(true);
            configuration.setCacheEnabled(false);
            sqlSessionFactory.setConfiguration(configuration);
    // 设置xml文件路径
            sqlSessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().
                    getResources("classpath*:mapper/*Mapper.xml"));
            //PerformanceInterceptor(),OptimisticLockerInterceptor()
            //添加分页功能
            sqlSessionFactory.setPlugins(new Interceptor[]{
                    paginationInterceptor()
            });
    //        sqlSessionFactory.setGlobalConfig(globalConfiguration()); //注释掉全局配置,因为在xml中读取就是全局配置
            return sqlSessionFactory.getObject();
        }
     
     /*   @Bean
        public GlobalConfiguration globalConfiguration() {
            GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
            conf.setLogicDeleteValue("-1");
            conf.setLogicNotDeleteValue("1");
            conf.setIdType(0);
            conf.setMetaObjectHandler(new MyMetaObjectHandler());
            conf.setDbColumnUnderline(true);
            conf.setRefresh(true);
            return conf;
        }*/
    }

      

    3、DBType枚举类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.df.openapi.config.db;
     
    public enum DBTypeEnum {
     
        db1("db1"), db2("db2");
        private String value;
     
        DBTypeEnum(String value) {
            this.value = value;
        }
     
        public String getValue() {
            return value;
        }
    }

      

    4、动态数据源决策

    1
    2
    3
    4
    5
    package com.df.openapi.config.db;
     
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     
    public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
    String datasource = DataSourceContextHolder.getDbType();
    LOGGER.debug("使用数据源 {}", datasource);
    return datasource;
    }
    1
    }

      

    5、设置、获取数据源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class DataSourceContextHolder {
     
        private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceContextHolder.class);
     
        private static final ThreadLocal contextHolder = new ThreadLocal<>(); //实际上就是开启多个线程,每个线程进行初始化一个数据源
        /**
         * 设置数据源
         * @param dbTypeEnum
         */
        public static void setDbType(DBTypeEnum dbTypeEnum) {
            contextHolder.set(dbTypeEnum.getValue());
        }
     
        /**
         * 取得当前数据源
         * @return
         */
        public static String getDbType() {
            return (String) contextHolder.get();
        }
     
        /**
         * 清除上下文数据
         */
        public static void clearDbType() {
            contextHolder.remove();
        }
    }

      

    6、AOP实现的数据源切换

    @Order设置的足够小是为了让他先执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    /**
     * aop的实现的数据源切换<br> * aop切点,实现mapper类找寻,找到所属大本营以后,如db1Aspect(),则会调用<br> * db1()前面之前的操作,进行数据源的切换。
     */
    @Component
    @Order(value = -100)
    @Slf4j
    @Aspect
    public class DataSourceAspect {
     
        @Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db1..*.*(..))")
        private void db1Aspect() {
        }
     
        @Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db2..*.*(..))")
        private void db2Aspect() {
        }
     
        @Before("db1Aspect()")
        public void db1() {
            log.info("切换到db1 数据源...");
            DataSourceContextHolder.setDbType(DBTypeEnum.db1);
        }
     
        @Before("db2Aspect()")
        public void db2() {
            log.info("切换到db2 数据源...");
            DataSourceContextHolder.setDbType(DBTypeEnum.db2);
        }
    }

      

    7、mapper层结构

    8、写一个service测试一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Service
    public class DictServiceImpl implements IDictService {
     
        @Resource
        private PtDictMapper ptDictMapper; //来自db1
     
        @Resource
        private SysDictMapper sysDictMapper; // 来自db2
     
     
        @Override
        public void getById(String id) {
            PtDict dict = ptDictMapper.selectById("2bf6257fc8fe483c84c1ad7e89d632f6");
            SysDict sysDict = sysDictMapper.getById("49");
            System.out.println("123");
        }
    }

     

    9、测试结果

     

    总结: 其实整个过程可以理解成,配置多数据源 xml中  -------> 然后通过加载多数源到spring工厂中-------->然后创建多线程,每个数据源对应一个数据源--------->然后实际调用时候,会先通过aop匹配到某一具体数据源------------->然后实例化当前数据源

    记得面试时候,有面试官会问道,你们多数据源是怎么实现的呀。.......,一阵蒙蔽中,然后说道我们之前项目中,没有用到多数据源。

    所幸,目前做得项目中有一个业务逻辑中,用到多个数据库数据情况,多数据源华丽上线。

    一. mybatis plus

          因为我们项目是springboot+mybatis plus,有些人一看,mybatis还知道对吧,mybatis plus是什么鬼,其实字面意思可以理解,就是对mybatis进行一些功能改造,一些封装升级,然后用起来特别方便。

         核心功能的升级主要是以下三点:

         支持通用的 CRUD、代码生成器与条件构造器。

          通用 CRUD:定义好 Mapper 接口后,只需要继承 BaseMapper<T> 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件
         条件构造器:通过 EntityWrapper<T> (实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的 SQL
         代码生成器:支持一系列的策略配置与全局配置,比 MyBatis 的代码生成更好用

    二.多数据源配置开始

        思路:

      1、yml中配置多个数据源信息

      2、通过AOP切换不同数据源

      3、配合mybatis plus使用

         

    1、yml配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    spring:
      aop:
          proxy-target-classtrue
          auto: true
      datasource:
        druid:
          db1:
            url: jdbc:mysql://localhost:3306/eboot
            username: root
            password: root
            driver-class-name: com.mysql.jdbc.Driver
            initialSize: 5
            minIdle: 5
            maxActive: 20
          db2:
            url: jdbc:oracle:thin:@192.168.136.222:ORCL
            username: sa
            password: sa123456
            driver-class-name: oracle.jdbc.OracleDriver
            initialSize: 5
            minIdle: 5
            maxActive: 20

      



    2、启动多个数据源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    @EnableTransactionManagement //开启事务
    @Configuration  //spring中常用到注解,与xml配置相对立。是两种加载bean方式
    @MapperScan("com.df.openapi.**.mapper.db*"// 扫描mapperdao的地址
    public class MybatisPlusConfig {
     
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
    //        paginationInterceptor.setLocalPage(true); // 由于版本问题,有些类可能招不到这个方法,需要升级jar包
            return paginationInterceptor;
        }
     
        @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) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DBTypeEnum.db1.getValue(), db1);
            targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
            dynamicDataSource.setTargetDataSources(targetDataSources);
            dynamicDataSource.setDefaultTargetDataSource(db2); // 程序默认数据源,这个要根据程序调用数据源频次,经常把常调用的数据源作为默认
            return dynamicDataSource;
        }
     
        @Bean("sqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
            sqlSessionFactory.setDataSource(multipleDataSource(db1(), db2()));
     
            MybatisConfiguration configuration = new MybatisConfiguration();
            configuration.setJdbcTypeForNull(JdbcType.NULL);
            configuration.setMapUnderscoreToCamelCase(true);
            configuration.setCacheEnabled(false);
            sqlSessionFactory.setConfiguration(configuration);
            //PerformanceInterceptor(),OptimisticLockerInterceptor()
            //添加分页功能
            sqlSessionFactory.setPlugins(new Interceptor[]{
                    paginationInterceptor()
            });
    //        sqlSessionFactory.setGlobalConfig(globalConfiguration()); //注释掉全局配置,因为在xml中读取就是全局配置
            return sqlSessionFactory.getObject();
        }
     
     /*   @Bean
        public GlobalConfiguration globalConfiguration() {
            GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
            conf.setLogicDeleteValue("-1");
            conf.setLogicNotDeleteValue("1");
            conf.setIdType(0);
            conf.setMetaObjectHandler(new MyMetaObjectHandler());
            conf.setDbColumnUnderline(true);
            conf.setRefresh(true);
            return conf;
        }*/
    }

      

    3、DBType枚举类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.df.openapi.config.db;
     
    public enum DBTypeEnum {
     
        db1("db1"), db2("db2");
        private String value;
     
        DBTypeEnum(String value) {
            this.value = value;
        }
     
        public String getValue() {
            return value;
        }
    }

      

    4、动态数据源决策

    1
    2
    3
    4
    5
    package com.df.openapi.config.db;
     
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     
    public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    @Override
    protected Object determineCurrentLookupKey() {
    String datasource = DataSourceContextHolder.getDbType();
    LOGGER.debug("使用数据源 {}", datasource);
    return datasource;
    }
    1
    }

      

    5、设置、获取数据源

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class DataSourceContextHolder {
     
        private static final Logger LOGGER = LoggerFactory.getLogger(DataSourceContextHolder.class);
     
        private static final ThreadLocal contextHolder = new ThreadLocal<>(); //实际上就是开启多个线程,每个线程进行初始化一个数据源
        /**
         * 设置数据源
         * @param dbTypeEnum
         */
        public static void setDbType(DBTypeEnum dbTypeEnum) {
            contextHolder.set(dbTypeEnum.getValue());
        }
     
        /**
         * 取得当前数据源
         * @return
         */
        public static String getDbType() {
            return (String) contextHolder.get();
        }
     
        /**
         * 清除上下文数据
         */
        public static void clearDbType() {
            contextHolder.remove();
        }
    }

      

    6、AOP实现的数据源切换

    @Order设置的足够小是为了让他先执行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    /**
     * aop的实现的数据源切换<br> * aop切点,实现mapper类找寻,找到所属大本营以后,如db1Aspect(),则会调用<br> * db1()前面之前的操作,进行数据源的切换。
     */
    @Component
    @Order(value = -100)
    @Slf4j
    @Aspect
    public class DataSourceAspect {
     
        @Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db1..*.*(..))")
        private void db1Aspect() {
        }
     
        @Pointcut("execution(* com.zwyl.bazhong.dao.mapper.db2..*.*(..))")
        private void db2Aspect() {
        }
     
        @Before("db1Aspect()")
        public void db1() {
            log.info("切换到db1 数据源...");
            DataSourceContextHolder.setDbType(DBTypeEnum.db1);
        }
     
        @Before("db2Aspect()")
        public void db2() {
            log.info("切换到db2 数据源...");
            DataSourceContextHolder.setDbType(DBTypeEnum.db2);
        }
    }

      

    7、mapper层结构

    8、写一个service测试一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Service
    public class DictServiceImpl implements IDictService {
     
        @Resource
        private PtDictMapper ptDictMapper; //来自db1
     
        @Resource
        private SysDictMapper sysDictMapper; // 来自db2
     
     
        @Override
        public void getById(String id) {
            PtDict dict = ptDictMapper.selectById("2bf6257fc8fe483c84c1ad7e89d632f6");
            SysDict sysDict = sysDictMapper.getById("49");
            System.out.println("123");
        }
    }

     

    9、测试结果

     

    总结: 其实整个过程可以理解成,配置多数据源 xml中  -------> 然后通过加载多数源到spring工厂中-------->然后创建多线程,每个数据源对应一个数据源--------->然后实际调用时候,会先通过aop匹配到某一具体数据源------------->然后实例化当前数据源

  • 相关阅读:
    Git——pull拉取远程指定分支以及push到远程指定分支
    Git——拉取远程主分支到本地新建分支,并关联到对应的远程新分支
    Git——基础学习
    Flutter——侧边二级菜单栏
    Flutter——static, final, const 区别
    Git一些常用的指令
    Flutter——切换页面,如何保持当前页的状态
    Flutter—找不到图片&不显示本地图片
    Android Studio快捷方式
    某iOS APP反抓包分析
  • 原文地址:https://www.cnblogs.com/handsome1013/p/13453443.html
Copyright © 2011-2022 走看看