美文网首页
java springboot动态数据源配置

java springboot动态数据源配置

作者: 进击的奥莉 | 来源:发表于2020-08-12 16:49 被阅读0次

    听说爱点赞的人都月瘦十斤,月入十万哟!
    自从接触了JAVA,真的是每天都学到新知识,很具有挑战性,今天开发过程中,springboot项目需要连接mysql数据库和sql server数据库.通过查资料,终于完成了接口测试,记录一下,以便帮助更多的人.
    通过百度查询 关键字:springboot 多数据源,给出的例子基本都是多套源策略,什么是多套源呢,看下图:

    多套源策略1.png
    这种策略虽然简单、直接、好理解,也符合开闭原则(再添加数据库,原来的数据库信息不用修改,只添加即可)
    但是资源浪费,代码冗余,缺乏灵活。需要针对每一个数据源写一套操作,不推荐使用。
    今天我来介绍使用AOP切面进行动态数据源配置。用户可以根据实际业务需要,统一操作逻辑,只需要在切换数据源的地方进行切换即可,超级方便。流程图如下:
    动态数据源2.png

    今天举例连接mysql数据库和sql server数据库

    1,包结构说明

    |--common
          |--annotation   //自定义注解
          |--aop     //切面
          |--context   //自定义
    |--config    //数据源配置
    |--controller    //访问接口
    |--entity   //实体类
    |--mapper  //数据库操作
    |--service    //服务类
          |--impl     //实现类
    

    2,pom.xml 引入相关包。

    其中sqlserver引入jar包比较特殊,不能直接maven引入,需要自己下载,并添加到本地maven默认地址,sqljdbc4:jar:4.0问题解决方案

          <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.2.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
                <version>5.1.47</version>
            </dependency>
    <!-- SqlServer -->
            <dependency>
                <groupId>com.microsoft.sqlserver</groupId>
                <artifactId>sqljdbc4</artifactId>
                <version>4.0</version>
            </dependency>
    <!-- 切面 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    

    3,接下来就是配置数据库信息了,在application.yml中添加

    注:这里可以看出mysql和sqlserver的配置是不同的。数据库名引入不同。
    mysql是端口号/book_test,
    sqlserver是端口号;DatabaseName=project

    spring:值对应
      datasource:
        master1:
          driver-class-name: com.mysql.jdbc.Driver
          jdbc-url: jdbc:mysql://localhost:3306/book_test?useUnicode=true&useSSL=false&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
          username: ***
          password: ***
        master2:
          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
          jdbc-url: jdbc:sqlserver://****:1433;DatabaseName=project
          username: **
          password: **
    

    4,前期准备工作已完成,接下来进行动态数据源配置

    4.1数据源配置:根据连接信息把数据源注入到spring中.config/DynamicDataSourceConfig.java
    package com.springboot.test.config;
    
    /**
     * @date: Created in 2020/8/12 13:36
     * @description: 动态数据源配置
     * @version: 1.0
     */
    //import me.mason.demo.dynamicdatasource.constants.DataSourceConstants;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.context.annotation.PropertySource;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    // 添加此配置,否则 报`The dependencies of some of the beans in the application context form a cycle`
    @EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
    @Configuration
    @PropertySource("classpath:application.yml")
    @MapperScan(basePackages = "com.springboot.test.mapper")
    public class DynamicDataSourceConfig {
    
        @Bean("master1")
        @ConfigurationProperties(prefix = "spring.datasource.master1")
        public DataSource master1DataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean("master2")
        @ConfigurationProperties(prefix = "spring.datasource.master2")
        public DataSource master2DataSource() {
            return DataSourceBuilder.create().build();
        }
    
        //设置动态数据源为主数据源
        @Bean
        @Primary
        public DataSource dynamicDataSource() {
            Map<Object, Object> dataSourceMap = new HashMap<>(2);
            dataSourceMap.put("master1", master1DataSource());
            dataSourceMap.put("master2", master2DataSource());
            //设置动态数据源
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            dynamicDataSource.setTargetDataSources(dataSourceMap);
            dynamicDataSource.setDefaultTargetDataSource(master1DataSource());
    
            return dynamicDataSource;
        }
    
    }
    

    4.2 添加动态数据源类.config/DynamicDataSource.java

    package com.springboot.test.config;
    
    import com.springboot.test.common.context.DynamicDataSourceContextHolder;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * @date: Created in 2020/8/12 14:07
     * @description: 动态数据源
     * @version: 1.0
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceContextHolder.getContextKey();
        }
    }
    

    4.3动态返回数据源。contxt/DynamicDataSourceContextHolder.java

    package com.springboot.test.common.context;
    
    /**
     * @date: Created in 2020/8/12 14:02
     * @description: TODO
     * @version: 1.0
     */
    public class DynamicDataSourceContextHolder {
    
        /**
         * 动态数据源名称上下文
         */
        private static final ThreadLocal<String> DATASOURCE_CONTEXT_KEY_HOLDER = new ThreadLocal<>();
    
        /**
         * 设置数据源
         * @param key
         */
        public static void setContextKey(String key){
            System.out.println("切换数据源"+key);
            DATASOURCE_CONTEXT_KEY_HOLDER.set(key);
        }
    
        /**
         * 获取数据源名称
         * @return
         */
        public static String getContextKey(){
            String key = DATASOURCE_CONTEXT_KEY_HOLDER.get();
            return key == null?"master1":key;
        }
    
        /**
         * 删除当前数据源名称
         */
        public static void removeContextKey(){
            DATASOURCE_CONTEXT_KEY_HOLDER.remove();
        }
    }
    

    4.3 定义数据源注解,这里很关键哟,定义了注解,之后再server层就可以直接@注解使用了.common/annotation/DS.java。不要问为什么把注解定义为java,因为大家都这样做,DS也是DataSource的简称。

    package com.springboot.test.common.annotation;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @date: Created in 2020/8/12 13:55
     * @description: TODO
     * @version: 1.0
     */
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DS {
        /**
         * 数据源名称
         * @return
         */
        String value() default "master1";
    }
    

    4.4 定义切面

    package com.springboot.test.common.aop;
    
    import com.springboot.test.common.annotation.DS;
    import com.springboot.test.common.context.DynamicDataSourceContextHolder;
    import org.aspectj.lang.ProceedingJoinPoint;
    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.springframework.stereotype.Component;
    
    import java.util.Objects;
    
    /**
     * @date: Created in 2020/8/12 14:00
     * @description: TODO
     * @version: 1.0
     */
    @Aspect
    @Component
    public class DynamicDataSourceAspect {
        @Pointcut("@annotation(com.springboot.test.common.annotation.DS)")
        public void dataSourcePointCut(){
    
        }
    
        @Around("dataSourcePointCut()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            String dsKey = getDSAnnotation(joinPoint).value();
            DynamicDataSourceContextHolder.setContextKey(dsKey);
            try{
                return joinPoint.proceed();
            }finally {
                DynamicDataSourceContextHolder.removeContextKey();
            }
        }
    
        /**
         * 根据类或方法获取数据源注解
         * @param joinPoint
         * @return
         */
        private DS getDSAnnotation(ProceedingJoinPoint joinPoint){
            Class<?> targetClass = joinPoint.getTarget().getClass();
            DS dsAnnotation = targetClass.getAnnotation(DS.class);
            // 先判断类的注解,再判断方法注解
            if(Objects.nonNull(dsAnnotation)){
                return dsAnnotation;
            }else{
                MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
                return methodSignature.getMethod().getAnnotation(DS.class);
            }
        }
    }
    

    5,接下来就是service层使用AOP进行数据源切换了。

    在service/impl/TestUserServiceImpl.java页面

    /**
         * 查询master1库User
         * @return
         */
        @DS("master1") //这个是自定义注解
        public List<TestUser> getMasterUser(){
            QueryWrapper<TestUser> queryWrapper = new QueryWrapper<>();
            return testUserMapper.selectAll(queryWrapper.isNotNull("name"));
        }
    
        /**
         * 查询master2库User
         * @return
         */
        @DS("master2")  //这个是自定义注解
        public List<TestUser> getSlaveUser(){
            return testUserMapper.selectList(null);
        }
    

    controller层

    /**
         * 查询全部
         */
        @GetMapping("/listall")
        public Object listAll() {
            int initSize = 2;
            Map<String, Object> result = new HashMap<>(initSize);
            //默认master数据源查询
            List<TestUser> masterUser = testUserService.getMasterUser();
            result.put("master1", masterUser);
            //从slave数据源查询
            List<TestUser> slaveUser = testUserService.getSlaveUser();
            result.put("master2", slaveUser);
            //返回数据
            return ResponseResult.success(result);
        }
    

    看,是不是超级简单?没有数据库切换代码,只需要关注业务逻辑即可,省心好多。你学会了吗?

    原文地址:https://segmentfault.com/a/1190000021613404

    相关文章

      网友评论

          本文标题:java springboot动态数据源配置

          本文链接:https://www.haomeiwen.com/subject/dnqmdktx.html