精华内容
下载资源
问答
  • 趣味编程|手写一个集成多数据源mongodb的 starter

    万次阅读 多人点赞 2021-01-31 09:59:37
    你还在为mongodb连接不上而发愁么,我都已经开始实现多数据源的mongo了,并且还封装了一个starter,开箱即用。

    关注公众号“AI码师”领取2021最新面试资料一份,公众号内回复“源码”,获取本项目源码

    【前言】

    主演:老王(技术总监),小码(本猿)

    老王:小码啊,我们项目中需要使用到mongodb,你集成下吧,完成了和我说下。

    小码:好的,一会就给你弄好。

    小码三下五除二的给集成好了,然后给老王汇报了。

    小码:王哥,我已经把mongodb集成好了。

    老王:好的,现在由于我们项目中会用到很多mongo数据库,你现在集成的mongo支持多数据源动态切换么?

    小码:这个,这个,啥叫多数据源动态切换啊?

    老王:就是在运行过程中,能够根据需要动态去连接哪个数据库,咱们项目需要支持多个特性,如果你对这个不太清楚的话,我给你一个思路,你可以考虑使用切面来实现,具体怎么弄,你自己研究下.

    小码:好的,王哥。

    小码想了很久,各种百度,终于找到了解决方案,花了一上午的时间,终于弄完了,又去给老王汇报了。

    小码:王哥,现在项目中的mongo已经实现了多数据源了(哈哈,心里很自豪)。

    老王:小伙子,很快嘛,不过现在又来一个任务,你需要把你集成的这个功能封装成一个starter,另外一个项目也需要使用这个功能,你抽时间封装下吧。

    小码:好的,王哥,保证完成任务

    小码下去之后,就开始研究怎么去封装成一个starter,下班之前弄好了,不过这次他没去找老王了,准备第二天再去,不然又得加班,哈哈!!!

    【正文】

    前面水了那么多,主要是给大家设置一种场景,让同志们知道为啥要去做这么一个功能,现在就直接进入正题了:

    【springboot集成mongodb】

    • 引入mongodb依赖
    <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    
    • 配置mongodb连接信息,在application.yml中配置
    # 设置了用户名和密码的连接
    spring:
      data:
        mongodb:
          uri: mongodb://用户名:密码@IP:PORT/数据库?authSource=${auth_db:用户认证数据库}
    # 没有设置用户名和密码的连接配置
    spring:
        data:
            mongodb:
                uri: mongodb://IP:PORT/数据库
    
    • 写测试代码

    我们创建一个接口,然后在接口方法中去操作monog库:

    接口中,直接引入MongoTemplate,就可以直接操作mongo了,这里对mongo如何使用不做过多介绍。

    /**
     * Created by AI码师 on 2019/4/19.
     * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
     * @return
     */
    @RequestMapping("/home")
    @RestController
    public class HomeController {
        @Autowired
        private MongoTemplate mongoTemplate;
        @PostMapping
        public String addData(@RequestParam(value = "name") String name,@RequestParam(value = "addr") String addr,@RequestParam(value = "email") String email){
            Student student = new Student();
            student.setAddr(addr);
            student.setName(name);
            student.setEmail(email);
            mongoTemplate.insert(student);
            return "添加成功";
        }
    }
    

    请求接口:

    在这里插入图片描述

    响应数据:
    在这里插入图片描述

    响应添加成功,我们看下数据库,是否添加上去了:

    在这里插入图片描述

    数据已经添加上去了,说明已经集成成功了,但这还是第一步,我们需要做的是支持多数据源,接下来我们一起来完成逼格更高的多数据源mongo吧。

    【实现多数据源】

    实现思路

    先介绍下实现多数据源动态切换的思路:

    首先通过AOP技术,在调用方法前后动态替换mongo数据源,这个主要是替换mongo中mongodbfactory(SimpleMongoClientDatabaseFactory)值,每个factory都维护自己需要连接的库,如果在操作之前,替换该参数为自己需要操作的数据库factory,操作结束又切换成原来的,不就可以实现动态切换数据源了么。

    说完了思路,我们直接上代码吧

    垒代码

    • 添加aop依赖
         <!--引入AOP依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
    • 修改数据库连接配置
    # 设置了用户名和密码的连接
    spring:
      data:
        mongodb:
          uri: mongodb://用户名:密码@IP:PORT/#?authSource=${auth_db:用户认证数据库}
    # 没有设置用户名和密码的连接配置
    spring:
        data:
            mongodb:
                uri: mongodb://IP:PORT/#
                
    与上述配置,做了小小的改动,将操作的数据库名称替换成了#,用来做后续备用
    
    • 创建切面
    package com.aimashi.dynamicmongo.config;
    
    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.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.data.mongodb.core.MongoDatabaseFactorySupport;
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    /**
     * Created by AI码师 on 2019/4/19.
     * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
     * @return
     */
    @Component
    @Aspect
    public class MongoSwitch {
      private final Logger logger = LoggerFactory.getLogger(MongoSwitch.class);
    
      @Autowired private MongoDatabaseFactorySupport mongoDbFactory;
      private final Map<String, MongoDatabaseFactorySupport> templateMuliteMap = new HashMap<>();
      // 获取配置文件的副本集连接
      @Value("${spring.data.mongodb.uri}")
      private String uri;
    
      //	@Pointcut("@annotation(com.pig4cloud.pig.common.log.annotation.MongoLog)")
      @Pointcut("execution(public * com.aimashi.dynamicmongo.config.MongotemplteService.*(..))")
      public void routeMongoDB() {}
    
      @Around("routeMongoDB()")
      public Object routeMongoDB(ProceedingJoinPoint joinPoint) {
        Object result = null;
        // 获取需要访问的项目数据库
        String dbName = (String) joinPoint.getArgs()[0];
        Object o = joinPoint.getTarget();
        Field[] fields = o.getClass().getDeclaredFields();
        MultiMongoTemplate mongoTemplate = null;
    
        try {
          for (Field field : fields) {
            field.setAccessible(true);
    
            Class fieldclass = field.getType();
            // 找到Template的变量
            if (fieldclass == MongoTemplate.class || fieldclass == MultiMongoTemplate.class) {
              // 查找项目对应的MongFactory
              SimpleMongoClientDatabaseFactory simpleMongoClientDbFactory = null;
              // 实例化
              if (templateMuliteMap.get(dbName) == null) { // 替换数据源
                simpleMongoClientDbFactory =
                    new SimpleMongoClientDatabaseFactory(this.uri.replace("#", dbName));
                templateMuliteMap.put(dbName, simpleMongoClientDbFactory);
              } else {
                simpleMongoClientDbFactory =
                    (SimpleMongoClientDatabaseFactory) templateMuliteMap.get(dbName);
              }
              // 如果第一次,赋值成自定义的MongoTemplate子类
              if (fieldclass == MongoTemplate.class) {
                mongoTemplate = new MultiMongoTemplate(simpleMongoClientDbFactory);
              } else if (fieldclass == MultiMongoTemplate.class) {
                Object fieldObject = field.get(o);
                mongoTemplate = (MultiMongoTemplate) fieldObject;
              }
              // 设置MongoFactory
              mongoTemplate.setMongoDbFactory(simpleMongoClientDbFactory);
              // 重新赋值
              field.set(o, mongoTemplate);
              break;
            }
          }
          try {
            result = joinPoint.proceed();
            // 清理ThreadLocal的变量
            mongoTemplate.removeMongoDbFactory();
          } catch (Throwable t) {
            logger.error("", t);
            mongoTemplate.removeMongoDbFactory();
          }
        } catch (Exception e) {
          logger.error("", e);
        }
    
        return result;
      }
    }
    
    • 创建相关配置类
    package com.aimashi.dynamicmongo.config;
    
    import org.springframework.data.mongodb.core.MongoTemplate;
    import org.springframework.stereotype.Service;
    /**
     * Created by AI码师 on 2019/4/19.
     * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
     * @return
     */
    @Service
    public class MongotemplteService {
      private MongoTemplate mongoTemplate;
      public <T> T save(String dbName, T var1) {
        return mongoTemplate.save(var1);
      }
    }
    
    package com.aimashi.dynamicmongo.config;
    
    import com.mongodb.client.MongoDatabase;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.data.mongodb.core.MongoDatabaseFactorySupport;
    import org.springframework.data.mongodb.core.MongoTemplate;
    
    public class MultiMongoTemplate extends MongoTemplate {
        private Logger logger= LoggerFactory.getLogger(MultiMongoTemplate.class);
    //用来缓存当前MongoDbFactory
        private static ThreadLocal<MongoDatabaseFactorySupport> mongoDbFactoryThreadLocal;
        public MultiMongoTemplate(MongoDatabaseFactorySupport mongoDbFactory){
            super(mongoDbFactory);
            if(mongoDbFactoryThreadLocal==null) {
                mongoDbFactoryThreadLocal = new ThreadLocal<>();
            }
        }
    
        public void setMongoDbFactory(MongoDatabaseFactorySupport factory){
            mongoDbFactoryThreadLocal.set(factory);
        }
    
        public void removeMongoDbFactory(){
            mongoDbFactoryThreadLocal.remove();
        }
    
        @Override
        public MongoDatabase getDb() {
            return mongoDbFactoryThreadLocal.get().getMongoDatabase();
        }
    }
    
    • 添加测试接口
    /**
     * Created by AI码师 on 2019/4/19.
     * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
     * @return
     */
    // dbName 为数据库名称
       @PutMapping
        public String addDataByDynamic(@RequestParam(value = "dbName") String dbName,@RequestParam(value = "name") String name,@RequestParam(value = "addr") String addr,@RequestParam(value = "email") String email){
            Student student = new Student();
            student.setAddr(addr);
            student.setName(name);
            student.setEmail(email);
            mongotemplteService.insert(dbName,student);
            return "添加成功";
        }
    

    请求接口:数据库名参数传了ams1

    在这里插入图片描述

    请求响应:响应成功
    在这里插入图片描述

    我们看下数据库,发现在数据库ams1下面已经有了此数据:

    在这里插入图片描述

    我们将数据库名参数修改为:ams2,进行请求

    在这里插入图片描述
    在这里插入图片描述

    发现数据源已经切换成功了。

    到这里,大家有没有发现自己很牛逼了啊,不过本篇文章还没算完,现在虽然已经实现了动态切换数据源的功能,但是还只能在自己项目上用,别的项目需要使用,只能直接复制过去,我们接下来需要做一个更牛逼的事情:手写一个starter来封装这个功能,别人只需要引入依赖,即可开箱即用:

    【整合到starter里面】

    • 创建一个maven项目:dynamicmongo-starter

    将如下文件拷贝到新项目中
    在这里插入图片描述

    • 创建自动装配文件
    package com.aimashi.dynamicmongo.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * Created by AI码师 on 2019/4/19.
     * 关注公众号【AI码师】领取2021最新面试资料一份(很全)
     * @return
     */
    @Configuration(proxyBeanMethods = false)
    public class MongodbAutoConfiguration {
    
    	@Bean
    	public MongoSwitch mongoSwitch() {
    		return new MongoSwitch();
    	}
    	@Bean
    	public MongotemplteService mongotemplteService() {
    		return new MongotemplteService();
    	}
    
    }
    
    • 新建 resources/META_INF/spring.factories 文件
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
        com.aimashi.dynamicmongo.config.MongodbAutoConfiguration
    

    到这里starter已经编写完成,是不是很简单。。

    【使用starter】

    starter已经编写好,我们只需要在项目中引入该依赖

            <dependency>
                <groupId>com.aimashi</groupId>
                <artifactId>dynamicmongo-starter</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
    

    然后在需要操作mongod方法的地方,引入:MongotemplteService即可;

    注意 MongotemplteService 里面的方法大家按需扩充,目前只写了一个,大家使用的时候,只需要把mongoTemplate里面的方法名写到MongotemplteService中,然后再去调用mongoTemplate里面对应方法即可。

    【总结】

    很少写这么长的实践类文章,现在已经十一点半了,该休息了,后面会有更多文章和大家一起分享,希望大家能有所收获,晚安!

    展开全文
  • spring boot多数据源配置,多数据源事务 springboot 中数据源配置,连接池配置,源码剖析,如何选择连接池 阅读本文前 ,请先阅读笔者另一片文章Spring Boot 源码深入分析 需求描述 在业务开发中,需要读写...

    欢迎关注本人公众号

    在这里插入图片描述

    springboot系列学习笔记全部文章请移步值博主专栏**: spring boot 2.X/spring cloud Greenwich
    由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。springboot系列代码全部上传至GitHub:https://github.com/liubenlong/springboot2_demo
    本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;

    需求描述

    在业务开发中,需要读写分离,或者需要配置多个数据源,接下来我们看看在springboot中如何配置多数据源,支持事务。

    想知道springboot如何加载配置以及选择使用哪个连接池,请移步springboot 中数据源配置,连接池配置,源码剖析,如何选择连接池

    application.properties修改如下,主要是添加双数据源:

    spring.datasource.master.url=${jdbc.master.url}
    spring.datasource.master.username=${jdbc.username}
    spring.datasource.master.password=${jdbc.password}
    spring.datasource.master.driverClassName=com.mysql.jdbc.Driver
    spring.datasource.master.max-idle=30
    spring.datasource.master.max-wait-millis=10000
    spring.datasource.master.min-idle=5
    spring.datasource.master.initial-size=5
    spring.datasource.master.initSQL=SELECT 2
    spring.datasource.master.connection-init-sqls=SELECT 1
    spring.datasource.master.validation-query=SELECT 1
    spring.datasource.master.validation-query-timeout=3000
    #spring.datasource.master.test-on-borrow=true
    spring.datasource.master.test-while-idle=true
    spring.datasource.master.time-between-eviction-runs-millis=10000
    spring.datasource.master.max-active=30
    
    
    spring.datasource.slave.url=${jdbc.slave.url}
    spring.datasource.slave.username=${jdbc.username}
    spring.datasource.slave.password=${jdbc.password}
    spring.datasource.slave.driverClassName=com.mysql.jdbc.Driver
    spring.datasource.slave.max-idle=30
    spring.datasource.slave.max-wait-millis=10000
    spring.datasource.slave.min-idle=5
    spring.datasource.slave.initial-size=5
    spring.datasource.slave.initSQL=SELECT 2
    spring.datasource.slave.connection-init-sqls=SELECT 1
    spring.datasource.slave.validation-query=SELECT 1
    spring.datasource.slave.validation-query-timeout=3000
    #spring.datasource.slave.test-on-borrow=true
    spring.datasource.slave.test-while-idle=true
    spring.datasource.slave.time-between-eviction-runs-millis=10000
    spring.datasource.slave.max-active=30
    

    然后如果想要springboot加载到这两个配置,需要自定义数据源以及事务管理器等:

    主库的配置:

    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    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.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    
    @Configuration
    @MapperScan(basePackages = {"repository的包名"}, sqlSessionTemplateRef  = "masterSqlSessionTemplate")
    public class MybatisMasterDbConfig {
    
        @Bean(name = "masterDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.master")
        @Primary
        public DataSource testDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "masterSqlSessionFactory")
        @Primary
        public SqlSessionFactory testSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/master/*.xml"));
            bean.setPlugins(new Interceptor[]{new EncryptionInterceptor()});
            return bean.getObject();
        }
    
        @Bean(name = "masterTransactionManager")
        @Primary
        public DataSourceTransactionManager testTransactionManager(@Qualifier("masterDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Bean(name = "masterSqlSessionTemplate")
        @Primary
        public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("masterSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    
    }
    

    从库的配置类:

    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    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.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    
    
    @Configuration
    @MapperScan(basePackages = {"repository的包名"}, sqlSessionTemplateRef = "slaveSqlSessionTemplate")
    public class MybatisSlaveDbConfig {
    
        @Bean(name = "slaveDataSource")
        @ConfigurationProperties(prefix = "spring.datasource.slave")
        public DataSource testDataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "sqlSessionFactory")
        @Primary
        public SqlSessionFactory testSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/slave/*.xml"));
            bean.setPlugins(new Interceptor[]{new EncryptionInterceptor()});
            return bean.getObject();
        }
    
        @Bean(name = "slaveTransactionManager")
        public DataSourceTransactionManager testTransactionManager(@Qualifier("slaveDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Bean(name = "slaveSqlSessionTemplate")
        public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("slaveSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    
    }
    

    可以看到,上述两个配置类,是根据包的路径来区分mapper的【我们这里使用的是mybatis,并且是使用的mapper.xml文件】。所以只需要建两个包路径即可:

    **.**.master.mapper
    **.**.slave.mapper
    

    假设master的mapper类下有UserMasterMapper.java
    假设slave的mapper类下有 UserSlaveMapper.java
    这两个mapper读者可以自行使用mysql的自动生成工具生成;对应上面配置类中的@MapperScan指定的basepackage地址。
    然后mapper.xml也应该有两个目录【在 masterSqlSessionFactory 中指定的目录地址】:
    假设resource/mapper/master下有UserMasterMapper.xml
    假设resource/mapper/slave下有UserMasterMapper.xml

    经过上面的配置,我们就可以愉快的撸代码了:

        @Autowired
        private UserMasterMapper userMasterMapper;
        @Autowired
        private UserSlaveMapper userSlaveMapper ;
    

    #事务
    接下来说一下事务问题。上面主从两个数据源的配置类中,各自配置了一个事务管理器masterSqlSessionTemplateslaveSqlSessionTemplate
    我们在写service方法的时候,一定要标识是使用的哪个事务管理器

    @Transactional(rollbackFor = Exception.class, transactionManager = "slaveTransactionManager")
        public void testMultiDST(String mobile) {
        }
    

    如果不加事务管理器,默认是第一个加载的数据源的事务。为了不出一些不可描述的BUG,强烈建议明确指定使用哪个事务管理器。

    配置项说明

    在application.properties配置文件中,我们可以看到

    #使用DBCP connection pool时,指定初始化时要执行的sql
    spring.datasource.slave.connection-init-sqls=SELECT 'x'
    #指定获取连接时连接校验的sql查询语句.
    spring.datasource.slave.validation-query=SELECT 'x'
    # 指定连接校验查询的超时时间.
    spring.datasource.slave.validation-query-timeout=3000 
    #获取连接时候验证,会影响性能(不建议true)
    spring.datasource.slave.test-on-borrow=false
    #验证连接的有效性
    spring.datasource.slave.test-while-idle=true
    #空闲连接回收的时间间隔,与test-while-idle一起使用,设置5分钟
    spring.datasource.slave.time-between-eviction-runs-millis=10000
    

    master也同样配置了。这几个参数是为了探测MySQL的链接是否存活,

    备注:单库的配置

    spring.datasource.url=${star.jdbc.url}
    spring.datasource.username=${jdbc.username}
    spring.datasource.password=${jdbc.password}
    spring.datasource.driverClassName=com.mysql.jdbc.Driver
    spring.datasource.tomcat.max-active=30
    spring.datasource.tomcat.max-idle=30
    spring.datasource.tomcat.max-wait=10000
    spring.datasource.tomcat.min-idle=5
    spring.datasource.tomcat.initial-size=5
    spring.datasource.tomcat.initSQL=SELECT 2
    spring.datasource.tomcat.validation-query=SELECT 'x'
    spring.datasource.tomcat.validation-query-timeout=3000
    #spring.datasource.tomcat.test-on-borrow=true
    spring.datasource.tomcat.test-while-idle=true
    spring.datasource.tomcat.time-between-eviction-runs-millis=10000
    logging.level.com.dy.springboot.server.mapper=debug
    

    springboot系列学习笔记全部文章请移步值博主专栏**: spring boot 2.X/spring cloud Greenwich
    由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。springboot系列代码全部上传至GitHub:https://github.com/liubenlong/springboot2_demo
    本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;

    参考资料

    https://www.cnblogs.com/xingzc/p/6022956.html

    展开全文
  • SpringBoot中多数据源的配置

    万次阅读 2018-07-27 15:11:08
    今天笔者就springboot中配置多数据源作个详细的讲解 2.实现方案 注意:一个应用工程中有且只有一个启动类,其依赖的模块不能是带有启动类的模块 ①application.yml配置 spring: datasource: druid: maste...

    1.场景还原

       在实际项目中,一个工程配置多个数据源很常见,工程可能会根据业务或者模块访问不同的数据库或表;今天笔者就springboot中配置多数据源作个详细的讲解

    2.实现方案

    注意:一个应用工程中有且只有一个启动类,其依赖的模块不能是带有启动类的模块

    ①application.yml配置

    spring:
      datasource:
        druid:
          master:  #数据源1
            url: jdbc:mysql://xxxx1:3306/online_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
            username: root
            password: root
          slave:  #数据源2
            url: jdbc:mysql://xxxxx2:3306/online_test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
            username: root
            password: root
          driverClassName: com.mysql.jdbc.Driver
          #最大活跃数
          maxActive: 20
          #初始化数量
          initialSize: 1
          #最大连接等待超时时间
          maxWait: 60000
          #打开PSCache,并且指定每个连接PSCache的大小
          poolPreparedStatements: true
          maxPoolPreparedStatementPerConnectionSize: 20
          #通过connectionProperties属性来打开mergeSql功能;慢SQL记录
          #connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
          minIdle: 1
          timeBetweenEvictionRunsMillis: 60000
          minEvictableIdleTimeMillis: 300000
          validationQuery: select 1 from dual
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          #配置监控统计拦截的filters,去掉后监控界面sql将无法统计,'wall'用于防火墙
          filters: stat, wall, log4j

    ②pom依赖

    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
    <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>5.0.7.RELEASE</version>
    </dependency>

    ③自定义多数据源注解

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataSource {
        String name() default "";
    }

    ④多数据源切面处理类,实现数据库的选择

    @Aspect
    @Component
    @Slf4j
    public class DataSourceAspect implements Ordered {
        
        @Pointcut("@annotation(com.yivi.yivisender.dispatchorder.conf.datasources.annotation.DataSource)")
        public void dataSourcePointCut() {
    
        }
    
        @Around("dataSourcePointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
    
            DataSource ds = method.getAnnotation(DataSource.class);
            if(ds == null){
                DynamicDataSource.setDataSource(DataSourceNames.MASTER);
                log.debug("set datasource is " + DataSourceNames.MASTER);
            }else {
                DynamicDataSource.setDataSource(ds.name());
                log.debug("set datasource is " + ds.name());
            }
    
            try {
                return point.proceed();
            } finally {
                DynamicDataSource.clearDataSource();
                log.debug("clean datasource");
            }
        }
    
        @Override
        public int getOrder() {
            return 1;
        }
    }

     

    public interface DataSourceNames {
        String MASTER = "master";
    
        String SLAVE = "slave";
    
    }

    ⑤DynamicDataSource的申明,其作为工程全局的datasource使用

    public class DynamicDataSource extends AbstractRoutingDataSource {
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    
        public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
            super.setDefaultTargetDataSource(defaultTargetDataSource);
            super.setTargetDataSources(targetDataSources);
            super.afterPropertiesSet();
        }
    
        @Override
        protected Object determineCurrentLookupKey() {
            return getDataSource();
        }
    
        public static void setDataSource(String dataSource) {
            contextHolder.set(dataSource);
        }
    
        public static String getDataSource() {
            return contextHolder.get();
        }
    
        public static void clearDataSource() {
            contextHolder.remove();
        }
    
    }

    ⑥动态数据源的配置

    @Configuration
    public class DynamicDataSourceConfig {
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid.master")
        public DataSource masterDataSource(){
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties("spring.datasource.druid.slave")
        public DataSource slaveDataSource(){
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @Primary
        public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
            Map<Object, Object> targetDataSources = new HashMap<>();
            targetDataSources.put(DataSourceNames.MASTER, masterDataSource);
            targetDataSources.put(DataSourceNames.SLAVE, slaveDataSource);
            return new DynamicDataSource(masterDataSource, targetDataSources);
        }
    }

    ⑦启动类的配置

    @SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
    @Import({DynamicDataSourceConfig.class})
    @ComponentScan("com.yivi")
    @MapperScan({"com.yivi.yivisender.dispatchorder.dao","com.yivi.yivisender.dispatchdata.dao","com.yivi.yivisender.dispatchcalengine.dao"})
    public class DispatchorderApplication extends SpringBootServletInitializer {
    
       public static void main(String[] args) {
          SpringApplication.run(DispatchorderApplication.class, args);
       }
    
       @Override
       protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
          return application.sources(DispatchorderApplication.class);
       }
    }
    

    3.测试效果

    ①多数据源测试类

    @Service
    public class DynasticServiceTest {
       @Autowired
       private YiViUserAccountMapper yiViUserAccountMapper;
    
        @DataSource(name = DataSourceNames.MASTER)
        public int addToMaster(String account ,String userId){
            return  yiViUserAccountMapper.increaseAccount(account,userId);
    
        }
    
        @DataSource(name = DataSourceNames.SLAVE)
        public int addToSlave(String account ,String userId){
            return  yiViUserAccountMapper.increaseAccount(account,userId);
    
        }
    }

    ②测试主库插入

    ③测试从库插入

    创作不易,莫要白嫖,您的关注及点赞是对于我创作的最大动力与源泉。

     

    展开全文
  • springboot-mybatis多数据源的配置方法

    万次阅读 2019-08-06 10:11:00
    springboot+mybatis配置多数据源,springboot版本在2.0以上 1.在application.yml中配置两个数据库: spring: datasource: master: jdbc-url: jdbc:postgresql://192.168.1.2:5432/test?useUnicode=true&...

    微信搜索:“二十同学” 公众号,欢迎关注一条不一样的成长之路

    springboot+mybatis配置多数据源,springboot版本在2.0以上

    1.在application.yml中配置两个数据库:

    spring:
      datasource:
        master:
          jdbc-url: jdbc:postgresql://192.168.1.2:5432/test?useUnicode=true&characterEncoding=utf8
          username: postgres
          password: postgres
          driverClassName: org.postgresql.Driver
          maxActive: 10
          maxIdle: 3
        worker:
          jdbc-url: jdbc:postgresql://192.168.1.3:5432/tests?useUnicode=true&characterEncoding=utf8
          username: postgres
          password: postgres
          driverClassName: org.postgresql.Driver
          maxActive: 10
          maxIdle: 3

    2.建立数据库配置文件,创建数据源

    
    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 javax.sql.DataSource;
    
    
    @Configuration
    public class DataSourceConfig {
    
        @Bean(name = "masterDB")
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource.master") // application.properteis中对应属性的前缀
        public DataSource masterDataSource() {
            return DataSourceBuilder.create().build();
        }
    
    
        @Bean(name = "workerDB")
        @ConfigurationProperties(prefix = "spring.datasource.worker") // application.properteis中对应属性的前缀
        public DataSource workerDataSource() {
            return DataSourceBuilder.create().build();
        }
    
    
    }

    3.配置mybatis的SqlSessionFactory分别使用不同的数据源:

    com.domain.mappers下的Mapper接口,都会使用masterDB数据源

    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import javax.sql.DataSource;
    
    
    @Configuration
    @MapperScan(basePackages = {"com.domain.mappers"}, sqlSessionFactoryRef = "sqlSessionFactory1")
    public class MybatisMasterConfig {
    
        @Autowired
        @Qualifier("masterDB")
        private DataSource mDB;
    
    
        @Bean
        @Primary
        public SqlSessionFactory sqlSessionFactory1() throws Exception {
    
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(mDB);
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:mappers/**/*.xml");
            factoryBean.setMapperLocations(resources);
            SqlSessionFactory sessionFactory = factoryBean.getObject();
            return sessionFactory;
    
        }
    
        @Bean
        @Primary
        public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
    
            SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory
            return template;
        }
    
    }

    com.domain.workmappers下的Mapper接口,都会使用workerDB数据源

    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.Resource;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    
    import javax.sql.DataSource;
    
    
    @Configuration
    @MapperScan(basePackages = {"com.domain.workmappers"}, sqlSessionFactoryRef = "sqlSessionFactory2")
    public class MybatisWorkerConfig {
    
        @Autowired
        @Qualifier("workerDB")
        private DataSource wDB;
    
        @Bean
        public SqlSessionFactory sqlSessionFactory2() throws Exception {
    
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(wDB);
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources("classpath*:workmappers/**/*.xml");
            factoryBean.setMapperLocations(resources);
            SqlSessionFactory sessionFactory = factoryBean.getObject();
            return sessionFactory;
    
    
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
            SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2());
            return template;
        }
    
    }

     

    展开全文
  • springMVC mybatis 多数据源配置

    万次阅读 2018-01-10 16:09:09
    springMVC mybatis 多数据源配置
  • Spark多数据源处理

    千人学习 2016-05-10 15:55:34
    Spark多数据源处理教程,该课程主要介绍如何通过Spark的DataSource API来读写外部数据源中的数据,并结合一些具体场景来分析和解释使用DataSource API的好处以及需要注意的问题。
  • 前言在开发过程中可能需要用到多个数据源,比如一个项目(MySQL)就是和(SQL Server)混合使用,就需要使用多数据源;如果业务场景比较复炸,可以使用动态数据源,灵活切换,典型的应用就是读写分离。下面分两个...
  • Spring Boot 集成Mybatis实现多数据源

    万次阅读 热门讨论 2017-06-30 19:12:38
    项目提交测试,趁着中当间的这个空档期,把springboot的多数据源配置学习一下,总体来说多数据源配置有两种方式,一种是静态的,一种是动态的。 静态的方式 我们以两套配置方式为例,在项目中有两套配置文件,两套...
  • 多数据源就是在一个程序服务里面,需发连接多个数据库。动态数据源在多数据源的基础上,要实现数据源的动态切换。这两种复杂系统的数据库联接有相关性,也有不同应用场景的区别。如要连接两个不同的业务库,一般会...
  • Mybatis配置多数据源

    万次阅读 2018-07-24 16:31:13
    在大多数情况下,一个功能全面的系统只使用单一数据源几乎是不太可能的,所以配置多数据源是十分必要的,记录一下去年某个项目配置多数据源的方式~ 单一数据源的配置如下: 1. 配置datasource 2. 配置...
  • 以前写过一篇教程,Springboot AOP方式切换多数据源(主从两库类似情况使用最佳): https://blog.csdn.net/qq_35387940/article/details/100122788 网上大多流传的springboot系列的切换多数据源都是以上那种写死...
  • 关于多数据源动态连接的问题,有一个主库,主库里有一个数据源表,我想通过查询这个数据源表然后动态连接到相应的数据库,请问怎么实现??好头疼啊。项目要求前端能添加删除修改数据源,后台不能写死
  • Spring Boot MyBatis 动态数据源切换、多数据源,读写分离https://blog.csdn.net/u013360850/article/details/78861442项目地址 ...本项目使用 Spring Boot 和 MyBatis 实现多数据源,动态数据源的...
  • springboot-mybatis多数据源的两种整合方法

    万次阅读 多人点赞 2018-07-17 18:23:15
    springboot-mybatis整合多数据源 简介: &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;随着并发量的不断增加,显然单个数据库已经承受不了高并发带来的...
  • 应用场景:项目使用多数据源,并实现读写分离。springboot默认加载application.properties或application.yml配置,配置规则已经定好且为单数据源,想要配置多数据源必须禁用默认加载,然后手动去配置多数据源,完整...
  • SpringBoot2.0之八 多数据源配置

    万次阅读 热门讨论 2018-04-27 09:50:34
      在开发的过程中我们可能都会遇到对接公司其他系统等需求,对于外部的系统可以采用接口对接的方式,对于一个公司开发的两个系统,并且知道相关数据库结构的情况下,就可以考虑使用多数据源来解决这个问题。...
  • SSM多数据源配置(配置两个数据源) 前言:SSM项目需要配置多数据源,以达到操作不同数据库的数据,网上看到大多是通过动态切换数据源的方式,但在项目中总会出现问题,这里通过配置两个spring-mybatis.xml...
  • spring boot 2.1 集成MySQL。 并且支持MySQL多数据源配置。 支持多数据源事务
  • 最近项目需要指出多数据源,同时支持事务回滚,这里记录一下 1、多数据源方式介绍 主要方式有以下两种: 通过配置多个SqlSessionFactory 来实现多数据源,这么做的话,未免过于笨重,而且无法实现动态添加数据源...
  • SpringBoot基于AOP多数据源

    千次阅读 2021-03-06 20:27:06
    SB整合MP多数据源 在实际工作过程中,可以会遇到需要配置动态数据源的问题,本小节提供SB整合MP的动态多数据源问题,支持service层接口、实现类、Mapper接口 添加数据源注解实现方案。本小节还在项目启动时添加了...
  • 多数据源的配置

    万次阅读 2018-10-23 20:14:06
    场景如下: 现在使用的是spring+mybatis+mysql 数据源只有一个,mysql的一个库;现在因为其中一个表dau_baseinfo的数据量太大,千万级别。...下面开始配置多数据源 第一步:创建一个DynamicDataSource的类,继...
  • 资源 Git地址:...Spring多数据源 Spring中,可以通过AbstractRoutingDataSource来实现多数据源管理。这里步骤为 1. 在Spring注册多个数据源 2. 新建类继承AbstractRoutingDataSource,并配置 3. 给Thead
  • SpringBoot多数据源问题

    2018-12-15 07:05:39
    目前在SpringBoot 配置多数据源的方式有两种: 1.分包,不用的业务放在不同的包下面,通过指定同包访问不同的数据源 2.通过自定义注解的方式,在每个方法或者类前面添加注解来实现访问不同的数据源 在...
  • 在Spring Boot 2.0配置多数据源和Spring Boot 1.5.x之前,一些配置及用法多少有些不同,其中一个问题就是“jdbcUrl is required with driverClassName.”的错误 解决办法: 在配置文件中使用spring.datasource....
  • spring boot和jpa整合,使用druid作为连接池,在单数据源时加密比较容易,但是多数据源时加密遇到问题,搜索了几个博文 https://blog.csdn.net/ooobama/article/details/80752223 文中给到了解决方案,就是手动解密...
  • SpringBoot整合MybatisPlus配置多数据源

    万次阅读 2020-01-07 11:55:24
    去年的时候公司的一个项目需要配置多数据源,当时使用的是springMVC框架,网上找了一些资料,花费了一些时间也整合了;今年公司使用了新的框架,现在的项目基本都是基于SpringBoot开发。以下来记录下自己使用...
  • SpringBoot多数据源切换,AOP实现动态数据源切换 操作数据一般都是在DAO层进行处理,可以选择直接使用JDBC进行编程 或者是使用多个DataSource 然后创建多个SessionFactory,在使用Dao层的时候通过不同的...
  • Spring Boot, Spring Cloud项目,变单数据源为多数据源配置问题
  • 网上关于动态数据源配置的博文一搜一大堆,都是拿...demo会在最后提供GitHub下载(博主 2018年3月16日14:26:47 注: 这种多数据源的动态切换确实可以解决数据的主从分库操作,但是却有一个致命的BUG,那就是事务不...
  • Sharding-JDBC多数据源动态切换

    万次阅读 热门讨论 2020-07-12 22:59:15
    Sharding-JDBC多数据源动态切换背景介绍方案介绍版本说明实现方式1. spring-boot依赖2. 启动类配置3. yml配置4. 自定义强制路由5. AOP切换数据源6. 编写事务代码7. 源码下载 背景介绍 我们公司项目使用多个mysql实例...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,950
精华内容 24,780
关键字:

多数据源