精华内容
下载资源
问答
  • 很久之前写一篇静态的springboot多数据源配置,一直没写下篇,这里补充一下自己用动态数据源的场景和开发逻辑。 之前说的静态数据源是每个数据源配置一套mapper,现在说说如何动态数据源共用一套mapper,以及...

    很久之前写一篇静态的springboot多数据源配置,一直没写下篇,这里补充一下自己用动态多数据源的场景和开发逻辑。

    之前说的静态多数据源是每个数据源配置一套mapper,现在说说如何动态多数据源共用一套mapper,以及数据源不从application.yml中配置,实现从数据库中读取数据源配置并动态切换数据源。

    springboot+mybatisplus多数据源配置和使用(一)

    一、先讲讲原理,了解AbstractRoutingDataSource类

    1.AbstractRoutingDataSource类的定义

    Spring提供了AbstractRoutingDataSource用于动态路由数据源,继承AbstractRoutingDataSource类并覆写其protected abstract Object determineCurrentLookupKey()即可。

    AbstractRoutingDataSource继承了AbstractDataSource,AbstractDataSource继承了DataSource

    所以该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上,即AbstractRoutingDataSource封装了(多个)DataSource,可以根据key找到对应的数据源

    2. AbstractRoutingDataSource实现动态多数据源的原理

    我们先看看AbstractRoutingDataSource类的源码,可以看到有几个入参。
    重要的入参是targetDataSources,和defaultTargetDataSource

    targetDataSources:保存多个数据源的map
    defaultTargetDataSource:指默认的数据源

    在这里插入图片描述
    首先targetDataSources是一个map,根据key保存不同的数据源,源码里面看到targetDataSources会转换成另一个map的变量resolvedDataSources,而defaultTargetDataSource转换成resolvedDefaultDataSource

    其实没什么变化,只是由Object类型转成了DataSource类型

    在这里插入图片描述
    重要的代码段如下

    protected DataSource determineTargetDataSource() {
            Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
            Object lookupKey = this.determineCurrentLookupKey();
            DataSource 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 + "]");
            } else {
                return dataSource;
            }
    }
        
    @Nullable
    protected abstract Object determineCurrentLookupKey();
    

    从这段代码可以看出,根据determineCurrentLookupKey()方法获取当前的数据源的key,然后根据这个lookupKey 去从存放有多个数据源的map的变量resolvedDataSources中取对应的dataSource,如果dataSource为null,则取默认的的resolvedDefaultDataSource作为数据源

    determineCurrentLookupKey是抽象方法,是由子类继承实现的。所以我们要实现动态多数据源,就继承AbstractRoutingDataSource,实现里面的determineCurrentLookupKey方法就相当于实现了动态多数据源的路由了


    我们改成这个determineCurrentLookupKey方法返回的key就相当于改变了数据源

    二、springboot使用动态多数据源

    从上面的原理解析,我们就知道实现动态多数据源,就需要写一个类继承AbstractRoutingDataSource类,重写里面的抽象方法determineCurrentLookupKey()即可,那么如何做呢?

    1. 继承AbstractRoutingDataSource,重写抽象方法

    /**
     * 多数据源
     *
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicContextHolder.peek();
        }
    
    }
    

    2. DynamicContextHolder操作数据源的key的上下文

    里面用了DynamicContextHolder.peek()返回数据源的key,而DynamicContextHolder是我们自己写的类,用于切换数据源的上下文的key,代码如下

    /**
     * 多数据源上下文
     *
     */
    public class DynamicContextHolder {
        @SuppressWarnings("unchecked")
        private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = new ThreadLocal() {
            @Override
            protected Object initialValue() {
                return new ArrayDeque();
            }
        };
    
        /**
         * 获得当前线程数据源
         *
         * @return 数据源名称
         */
        public static String peek() {
            return CONTEXT_HOLDER.get().peek();
        }
    
        /**
         * 设置当前线程数据源
         *
         * @param dataSource 数据源名称
         */
        public static void push(String dataSource) {
            CONTEXT_HOLDER.get().push(dataSource);
        }
    
        /**
         * 清空当前线程数据源
         */
        public static void poll() {
            Deque<String> deque = CONTEXT_HOLDER.get();
            deque.poll();
            if (deque.isEmpty()) {
                CONTEXT_HOLDER.remove();
            }
        }
    
    }
    

    这个DynamicContextHolder类的peek()是返回数据源的key名称,push是设置数据源的名称,然后我们只要操作这个类的push方法改变数据源的名称key,就相当于切换了数据源。

    1. 为什么里面使用了ThreadLocal
      ThreadLocal是指当前线程的本地变量,每个线程里面都有一个自己的变量,其他线程无法操作当前线程的这个变量。确保当前访问请求的线程不会被其他线程改变这个当前线程的数据。
    2. 我们在哪个位置使用DynamicContextHolder.push来改变数据源的key呢
      我们利用aop,再写一个注解,把注解加在相应的service方法上,通过这个注解的切面类来调用DynamicContextHolder.push,从而改变determineCurrentLookupKey,从而切换数据源

    3.application.yml的配置

    spring:
        datasource:
            type: com.alibaba.druid.pool.DruidDataSource
            druid:
                driver-class-name: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://192.168.33.76:3306/database?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
                username: master
                password: 123456
                initial-size: 10
                max-active: 100
                min-idle: 10
                max-wait: 60000
                pool-prepared-statements: true
                max-pool-prepared-statement-per-connection-size: 20
                time-between-eviction-runs-millis: 60000
                min-evictable-idle-time-millis: 300000
                #Oracle需要打开注释
                #validation-query: SELECT 1 FROM DUAL
                test-while-idle: true
                test-on-borrow: false
                test-on-return: false
                stat-view-servlet:
                    enabled: true
                    url-pattern: /druid/*
                    #login-username: admin
                    #login-password: admin
                filter:
                    stat:
                        log-slow-sql: true
                        slow-sql-millis: 1000
                        merge-sql: false
                    wall:
                        config:
                            multi-statement-allow: true
    
    
    ##多数据源的配置
    dynamic:
      datasource:
        slave1:
          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
          url: jdbc:mysql://192.168.33.76:3306/database1?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
          username: test1
          password: 123456
        slave2:
          driver-class-name: org.postgresql.Driver
          url: jdbc:mysql://192.168.33.76:3306/database2?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
          username: test2
          password: 123456
    

    4. 配置我们自定义的DynamicDataSource类,注入spring容器

    主要的方法是
    DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties)
    相当于在spring容器中配置这个bean的实例

        @Bean
        public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            //设置多个数据源的map
            dynamicDataSource.setTargetDataSources(getDynamicDataSource());
            //默认数据源
            DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
            dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
            return dynamicDataSource;
        }
    

    整个类的全部代码如下,核心代码为:dynamicDataSource.setTargetDataSources(getDynamicDataSource())

    下面代码看出,先把多个数据源配置从appliaiton.yml读出出来,然后转成targetDataSources

    /**
     * 配置多数据源
     *
     */
    @Configuration
    @EnableConfigurationProperties(DynamicDataSourceProperties.class)
    public class DynamicDataSourceConfig {
        @Autowired
        private DynamicDataSourceProperties properties;
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.druid")
        public DataSourceProperties dataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            //设置多个数据源的map
            dynamicDataSource.setTargetDataSources(getDynamicDataSource())
            //默认数据源
            DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
            dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
            return dynamicDataSource;
        }
    
        private Map<Object, Object> getDynamicDataSource(){
            Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource();
            Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
            dataSourcePropertiesMap.forEach((k, v) -> {
                DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
                targetDataSources.put(k, druidDataSource);
            });
    
            return targetDataSources;
        }
    
    }
    
    

    其他涉及的类

    /**
     * 多数据源属性(从applition.yml读取数据源配置)
     *
     */
    @ConfigurationProperties(prefix = "dynamic")
    public class DynamicDataSourceProperties {
        private Map<String, DataSourceProperties> datasource = new LinkedHashMap<>();
    
        public Map<String, DataSourceProperties> getDatasource() {
            return datasource;
        }
    
        public void setDatasource(Map<String, DataSourceProperties> datasource) {
            this.datasource = datasource;
        }
    }
    
    
    /**
     * 将数据源配置构建实际的数据源
     *
     */
    public class DynamicDataSourceFactory {
    
        public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) {
            DruidDataSource druidDataSource = new DruidDataSource();
            druidDataSource.setDriverClassName(properties.getDriverClassName());
            druidDataSource.setUrl(properties.getUrl());
            druidDataSource.setUsername(properties.getUsername());
            druidDataSource.setPassword(properties.getPassword());
    
            druidDataSource.setInitialSize(properties.getInitialSize());
            druidDataSource.setMaxActive(properties.getMaxActive());
            druidDataSource.setMinIdle(properties.getMinIdle());
            druidDataSource.setMaxWait(properties.getMaxWait());
            druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());
            druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());
            druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());
            druidDataSource.setValidationQuery(properties.getValidationQuery());
            druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());
            druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());
            druidDataSource.setTestOnReturn(properties.isTestOnReturn());
            druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());
            druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());
            druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements());
    
            try {
                druidDataSource.setFilters(properties.getFilters());
                druidDataSource.init();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return druidDataSource;
        }
    }
    

    5. 通过注解切换数据源

    上面已经完成了大部分逻辑了,还有最重要的一步,如何通过aop切换数据源

    1)先写一个注解

    /**
     * 多数据源注解
     *
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface DataSource {
        String value() default "";
    }
    
    

    2)再写这个注解的切面类

    下面的代码很简单,可以看出,所有使用了这个注解的都会经过这个切面类,这个切面类先判断注解是在方法上还是类上,如果方法上有注解优先使用方法上的,获取注解的value属性的值,把这个值作为数据源的key。

    通过 DynamicContextHolder.push(value)来设置数据源的key(这里改变后, determineCurrentLookupKey()重写的方法返回的key也就改变了,从而切换了数据源)

    /**
     * 多数据源,切面处理类
     *
     */
    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class DataSourceAspect {
        protected Logger logger = LoggerFactory.getLogger(getClass());
    
        @Pointcut("@annotation(io.renren.datasource.annotation.DataSource) " +
                "|| @within(io.renren.datasource.annotation.DataSource)")
        public void dataSourcePointCut() {
    
        }
    
        @Around("dataSourcePointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Class targetClass = point.getTarget().getClass();
            Method method = signature.getMethod();
    
            DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
            DataSource methodDataSource = method.getAnnotation(DataSource.class);
            if(targetDataSource != null || methodDataSource != null){
                String value;
                if(methodDataSource != null){
                    value = methodDataSource.value();
                }else {
                    value = targetDataSource.value();
                }
    
                DynamicContextHolder.push(value);
                logger.debug("set datasource is {}", value);
            }
    
            try {
                return point.proceed();
            } finally {
                DynamicContextHolder.poll();
                logger.debug("clean datasource");
            }
        }
    }
    

    3)把这个注解加在需要使用多数据源的service方法或类上

    注解加在controller上的类或方法也可以的

    注解不是很懂的话,可以看这篇文章:springboot项目中自定义注解的使用总结

    @Service
    //@DataSource("slave1")
    public class DynamicDataSourceTestService {
        @Autowired
        private SysUserDao sysUserDao;
    
        @Transactional
        public void updateUser(Long id){
            SysUserEntity user = new SysUserEntity();
            user.setUserId(id);
            user.setMobile("13500000000");
            sysUserDao.updateById(user);
        }
    
        @Transactional
        @DataSource("slave1")
        public void updateUserBySlave1(Long id){
            SysUserEntity user = new SysUserEntity();
            user.setUserId(id);
            user.setMobile("13500000001");
            sysUserDao.updateById(user);
        }
    
        @DataSource("slave2")
        @Transactional
        public void updateUserBySlave2(Long id){
            SysUserEntity user = new SysUserEntity();
            user.setUserId(id);
            user.setMobile("13500000002");
            sysUserDao.updateById(user);
        }
    }
    

    从上面的三个方法逐个调用测试,可以看到操作了三个不同的数据源的数据

    三、从数据库中读取动态多数据源的配置

    这里还有挺多东西的,有点长,在下篇博客总结
    springboot动态多数据源配置和使用(从数据库读取数据源配置)(三)

    参考:
    这里的代码参考了人人开源项目的renren-fast项目,要想看全部源码,可以通过git把项目下载回来看看
    项目:https://gitee.com/renrenio/renren-fast.git

    展开全文
  • 上两遍已经描述了动态多数据源的原理和基础实现了,上面两篇都是从applition.yml中配置多数据源的,这里再拓展补充一下其他场景,如何读取数据源不从applition.yml中配置,实现从数据库中读取数据源配置并动态切换...

    上两遍已经描述了动态多数据源的原理和基础实现了,前面的数据源配置都是从application.yml中配置多数据源的,这里再拓展补充一下其他场景,如何读取数据源不从application.yml中配置,实现从数据库中读取数据源配置并动态切换数据源。

    一、回顾上篇的动态多数据源配置

    上篇:springboot动态多数据源配置和使用(二)

    1. 继承AbstractRoutingDataSource,重写抽象方法determineCurrentLookupKey()
    /**
     * 多数据源
     *
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicContextHolder.peek();
        }
    
    }
    
    1. 注入spring容器
        @Bean
        public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            //设置多个数据源的map
            dynamicDataSource.setTargetDataSources(getDynamicDataSource())
            //默认数据源
            DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
            dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
            return dynamicDataSource;
        }
    
        private Map<Object, Object> getDynamicDataSource(){
            Map<String, DataSourceProperties> dataSourcePropertiesMap = properties.getDatasource();
            Map<Object, Object> targetDataSources = new HashMap<>(dataSourcePropertiesMap.size());
            dataSourcePropertiesMap.forEach((k, v) -> {
                DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v);
                targetDataSources.put(k, druidDataSource);
            });
    
            return targetDataSources;
        }
    
    
    1. 可以看到多数据源配置是从application.yml读取出来,然后设置到DynamicDataSource对象里的targetDataSources属性
      dynamicDataSource.setTargetDataSources();

    分析

    从上面分析可以知道,重要的是targetDataSources这个存放多数据源的map属性。
    那么我们只要把targetDataSources这个map由配置文件获取创建dataSource然后放入map改写成由数据读读取出来的配置,再创建dataSource再放入targetDataSources这个map变量就可以实现我们想要的功能了。

    二、从数据库获取配置创建数据源

    这一步说难也不难,就把数据库的配置保存在数据库的表里面,在切面类切换数据源时读取数据库的配置,然后创建数据源,把创建的数据源通过put方法放入targetDataSources这个map即可,最后在切面类DynamicContextHolder.push(key)改变数据源

    但是这样子就很没效率,每次都从数据库读取配置,然后创建dataSource数据源。所以实际上我们是懒加载的模式,再用一个数据源缓存池pool来保存dataSource,如果缓存有了dataSource就不再从数据库读取了,直接从数据源缓存池的pool来获取数据源。

    具体实现

    1.创建表

    CREATE TABLE `oa_quick_knife_datasource` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `datasource_name` varchar(20) DEFAULT '' COMMENT '数据源名称',
      `datasource_url` varchar(200) DEFAULT '' COMMENT '数据源url',
      `datasource_account` varchar(50) DEFAULT '' COMMENT '数据源帐号',
      `datasource_password` varchar(64) DEFAULT '' COMMENT '数据源密码',
      `remark` varchar(200) DEFAULT NULL COMMENT '备注',
      `is_show_type` tinyint(1) DEFAULT NULL COMMENT '数据源可见类型(1-全部人可见,2-部分人可见)',
      `datasource_type` tinyint(1) DEFAULT NULL COMMENT '默认mysql,暂时只支持mysql',
      `update_name` varchar(20) DEFAULT '' COMMENT '更新人',
      `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
      `create_code` int(11) unsigned DEFAULT '0' COMMENT '创建人工号',
      `create_name` varchar(20) DEFAULT '' COMMENT '创建人',
      `update_code` int(11) unsigned DEFAULT '0' COMMENT '更新人工号',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      `deleted_flag` tinyint(1) DEFAULT '0' COMMENT '是否删除',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='数据源 ';
    

    注:datasource_password这个字段我们不要明文保存数据库密码,我们加密后再放入这个字段里面

    2.数据源缓存池

    数据源缓存池的类代码

    /**
     * 数据源缓存池
     */
    public class DataSourceCachePool {
        /** 数据源连接池缓存【本地 class缓存 - 不支持分布式】 */
        private static Map<String, DruidDataSource> dbSources = new HashMap<>();
        private static RedisTemplate<String, Object> redisTemplate;
    
        private static RedisTemplate<String, Object> getRedisTemplate() {
            if (redisTemplate == null) {
                redisTemplate = (RedisTemplate<String, Object>) SpringContextUtils.getBean("redisTemplate");
            }
            return redisTemplate;
        }
    
        /**
         * 获取多数据源缓存
         *
         * @param dbKey
         * @return
         */
        public static DynamicDataSourceModel getCacheDynamicDataSourceModel(String dbKey) {
            String redisCacheKey = ConfigConstant.SYS_DYNAMICDB_CACHE + dbKey;
            if (getRedisTemplate().hasKey(redisCacheKey)) {
                String model = (String)getRedisTemplate().opsForValue().get(redisCacheKey);
                return  JSON.parseObject(model,DynamicDataSourceModel.class);
            }
            DatasourceDao datasourceDao = (DatasourceDao)SpringContextUtils.getBean("datasourceDao");
            DynamicDataSourceModel dbSource = datasourceDao.getDynamicDbSourceByCode(dbKey);
            try{
                dbSource.setDbPassword(AesUtil.decryptBySalt(dbSource.getDbPassword(),dbSource.getId()));
            }catch (Exception e){
                throw new RRException("动态数据源密钥解密失败,dbKey:"+dbKey);
            }
    
            if (dbSource != null) {
                getRedisTemplate().opsForValue().set(redisCacheKey, JSONObject.toJSONString(dbSource));
            }
            return dbSource;
        }
    
        public static DruidDataSource getCacheBasicDataSource(String dbKey) {
            return dbSources.get(dbKey);
        }
    
        /**
         * put 数据源缓存
         *
         * @param dbKey
         * @param db
         */
        public static void putCacheBasicDataSource(String dbKey, DruidDataSource db) {
            dbSources.put(dbKey, db);
        }
    
        /**
         * 清空数据源缓存
         */
        public static void cleanAllCache() {
            //关闭数据源连接
            for(Map.Entry<String, DruidDataSource> entry : dbSources.entrySet()){
                String dbkey = entry.getKey();
                DruidDataSource druidDataSource = entry.getValue();
                if(druidDataSource!=null && druidDataSource.isEnable()){
                    druidDataSource.close();
                }
                //清空redis缓存
                getRedisTemplate().delete(ConfigConstant.SYS_DYNAMICDB_CACHE + dbkey);
            }
            //清空缓存
            dbSources.clear();
        }
    
        public static void removeCache(String dbKey) {
            //关闭数据源连接
            DruidDataSource druidDataSource = dbSources.get(dbKey);
            if(druidDataSource!=null && druidDataSource.isEnable()){
                druidDataSource.close();
            }
            //清空redis缓存
            getRedisTemplate().delete(ConfigConstant.SYS_DYNAMICDB_CACHE + dbKey);
            //清空缓存
            dbSources.remove(dbKey);
        }
    
    }
    

    上面的数据源缓存池主要代码是下面getCacheDynamicDataSourceModel方法的这段

    这个方法的逻辑是先从redis缓存数据源配置,redis没有则从数据库获取,以及获取的配置的数据库密码是加密的,所以这里还要再解密

        /**
         * 获取多数据源缓存配置
         *
         * @param dbKey
         * @return
         */
        public static DynamicDataSourceModel getCacheDynamicDataSourceModel(String dbKey) {
            String redisCacheKey = ConfigConstant.SYS_DYNAMICDB_CACHE + dbKey;
            if (getRedisTemplate().hasKey(redisCacheKey)) {
                String model = (String)getRedisTemplate().opsForValue().get(redisCacheKey);
                return  JSON.parseObject(model,DynamicDataSourceModel.class);
            }
            DatasourceDao datasourceDao = (DatasourceDao)SpringContextUtils.getBean("datasourceDao");
            DynamicDataSourceModel dbSource = datasourceDao.getDynamicDbSourceByCode(dbKey);
            try{
                dbSource.setDbPassword(AesUtil.decryptBySalt(dbSource.getDbPassword(),dbSource.getId()));
            }catch (Exception e){
                throw new RRException("动态数据源密钥解密失败,dbKey:"+dbKey);
            }
    
            if (dbSource != null) {
                getRedisTemplate().opsForValue().set(redisCacheKey, JSONObject.toJSONString(dbSource));
            }
            return dbSource;
        }
    

    还有一个重要的方法,把数据源放入缓存池的dbSource这个map属性里面

        /**
         * put 数据源缓存
         *
         * @param dbKey
         * @param db
         */
        public static void putCacheBasicDataSource(String dbKey, DruidDataSource db) {
            dbSources.put(dbKey, db);
        }
    

    3.再写一个工具类DynamicDBUtil 操作数据源缓存池

    这个类的核心方法是getDbSourceByDbKey(),先判断缓存池有没有对应key的数据源,没有则读取数据源配置(先从redis读配置,没有再从数据库读配置),根据配置创建DruidDataSource数据源,再把数据源放入缓存池

    getDbSourceByDbKey这个方法的dbKey是指能根据这个key找到数据库对应的记录,这里指该表的id

    /**
     * Spring JDBC 实时数据库访问
     *
     */
    @Slf4j
    public class DynamicDBUtil {
    
        /**
         * 通过 dbKey ,获取数据源
         *
         * @param dbKey
         * @return
         */
        public static DruidDataSource getDbSourceByDbKey(final String dbKey) {
    
            //先判断缓存中是否存在数据库链接
            DruidDataSource cacheDbSource = DataSourceCachePool.getCacheBasicDataSource(dbKey);
            if (cacheDbSource != null && !cacheDbSource.isClosed()) {
                log.debug("--------getDbSourceBydbKey------------------从缓存中获取DB连接-------------------");
                return cacheDbSource;
            } else {
                //获取多数据源配置
                DynamicDataSourceModel dbSource = DataSourceCachePool.getCacheDynamicDataSourceModel(dbKey);
                DruidDataSource dataSource = getJdbcDataSource(dbSource);
                if(dataSource!=null && dataSource.isEnable()){
                    DataSourceCachePool.putCacheBasicDataSource(dbKey, dataSource);
                }else{
                    throw new RRException("动态数据源连接失败,dbKey:"+dbKey);
                }
                log.info("--------getDbSourceBydbKey------------------创建DB数据库连接-------------------");
                return dataSource;
            }
        }
    
        /**
         * 获取数据源【最底层方法,不要随便调用】
         *
         * @param dbSource
         * @return
         */
        private static DruidDataSource getJdbcDataSource(final DynamicDataSourceModel dbSource) {
            DruidDataSource dataSource = new DruidDataSource();
    
            String driverClassName = dbSource.getDbDriver();
            String url = dbSource.getDbUrl();
            String dbUser = dbSource.getDbUsername();
            String dbPassword = dbSource.getDbPassword();
            dataSource.setDriverClassName(driverClassName);
            dataSource.setUrl(url);
            //dataSource.setValidationQuery("SELECT 1 FROM DUAL");
            dataSource.setTestWhileIdle(true);
            dataSource.setTestOnBorrow(false);
            dataSource.setTestOnReturn(false);
            dataSource.setBreakAfterAcquireFailure(true);
            dataSource.setConnectionErrorRetryAttempts(0);
            dataSource.setUsername(dbUser);
            dataSource.setMaxWait(60000);
            dataSource.setPassword(dbPassword);
    
            log.info("******************************************");
            log.info("*                                        *");
            log.info("*====【"+dbSource.getCode()+"】=====Druid连接池已启用 ====*");
            log.info("*                                        *");
            log.info("******************************************");
            return dataSource;
        }
    
        /**
         * 关闭数据库连接池
         *
         * @param dbKey
         * @return
         */
        public static void closeDbKey(final String dbKey) {
            DruidDataSource dataSource = getDbSourceByDbKey(dbKey);
            try {
                if (dataSource != null && !dataSource.isClosed()) {
                    dataSource.getConnection().commit();
                    dataSource.getConnection().close();
                    dataSource.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
    
        private static JdbcTemplate getJdbcTemplate(String dbKey) {
            DruidDataSource dataSource = getDbSourceByDbKey(dbKey);
            return new JdbcTemplate(dataSource);
        }
    
        /**
         * 获取连接
         * @param url
         * @param username
         * @param password
         * @param driverName
         * @return
         */
        public static Connection getConn(String url,String username,String password,String driverName) {
            Connection conn = null;
            try {
                Class.forName(driverName);
                conn = DriverManager.getConnection(url, username, password);
            } catch (Exception e) {
                throw new RRException("无法连接,问题:"+e.getMessage(), e);
            }
    
            return conn;
        }
    
        /**
         * 关闭数据库连接
         * @param
         */
        public static void closeConnection(Connection conn) {
            try {
                if(conn!=null){
                    conn.close();
                }
            } catch (SQLException e) {
                throw new RRException("close connection failure", e);
            }
        }
    
    }
    

    4. 继承继承AbstractRoutingDataSource,重写抽象方法determineCurrentLookupKey()

    这里比上一篇的DynamicDataSource新增了targetDataSources静态变量和setDataSource()静态方法。

    targetDataSources这个属性用于存放多数据源

    /**
     * 多数据源
     *
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
    
        public static Map<Object, Object> targetDataSources = new ConcurrentHashMap<>(10);
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicContextHolder.peek();
        }
    
        public static void setDataSource(String dbKey) throws Exception{
            if(!DynamicDataSource.targetDataSources.containsKey(dbKey)){
                DruidDataSource dataSource = DynamicDBUtil.getDbSourceByDbKey(dbKey);
                DynamicDataSource.targetDataSources.put(dbKey,dataSource);
            }
            //切换动态多数据源的dbKey
            DynamicContextHolder.push(dbKey);
            DynamicDataSource dynamicDataSource = (DynamicDataSource) SpringContextUtils.getBean("dynamicDataSource");
            //使得修改后的targetDataSources生效
            dynamicDataSource.afterPropertiesSet();
        }
    
    }
    

    5. 数据源配置类

    下面代码通过dynamicDataSource.setTargetDataSources(DynamicDataSource.targetDataSources)把值引用赋值给dynamicDataSource对象(即指向同一块内存,修改了静态变量targetDataSources,就相当于修改了dynamicDataSource对象里面的targetDataSources属性)

    @Configuration
    @EnableConfigurationProperties(DynamicDataSourceProperties.class)
    public class DynamicDataSourceConfig {
        @Autowired
        private DynamicDataSourceProperties properties;
    
        @Bean
        @ConfigurationProperties(prefix = "spring.datasource.druid")
        public DataSourceProperties dataSourceProperties() {
            return new DataSourceProperties();
        }
    
        @Bean
        public DruidDataSource defaultDataSource(DataSourceProperties dataSourceProperties) {
            //默认数据源,通过配置获取创建
            DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties);
            return defaultDataSource;
        }
    
        @Bean
        @Primary
        @DependsOn({"defaultDataSource"})
        public DynamicDataSource dynamicDataSource(DruidDataSource defaultDataSource) {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            //设置targetDataSources(通过数据库配置获取,首次创建没有数据源)
            dynamicDataSource.setTargetDataSources(DynamicDataSource.targetDataSources);
    
            //默认数据源
            dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);
            return dynamicDataSource;
        }
    
    }
    

    6. 再写一个注解以及实现这个注解的切面类就可以了

    /**
     * 多数据源注解
     *
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface DataSource {
        String value() default "";
    }
    
    /**
     * 多数据源,切面处理类
     *
     */
    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class DataSourceAspect {
        protected Logger logger = LoggerFactory.getLogger(getClass());
    
        @Pointcut("@annotation(io.renren.datasource.annotation.DataSource) " +
                "|| @within(io.renren.datasource.annotation.DataSource)")
        public void dataSourcePointCut() {
    
        }
    
        @Around("dataSourcePointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Class targetClass = point.getTarget().getClass();
            Method method = signature.getMethod();
    
            DataSource targetDataSource = (DataSource)targetClass.getAnnotation(DataSource.class);
            DataSource methodDataSource = method.getAnnotation(DataSource.class);
            if(targetDataSource != null || methodDataSource != null){
                String value;
                if(methodDataSource != null){
                    value = methodDataSource.value();
                }else {
                    value = targetDataSource.value();
                }
                //根据dbKey动态设置数据源
                DynamicDataSource.setDataSource(dbKey);
                logger.debug("set datasource is {}", value);
            }
    
            try {
                return point.proceed();
            } finally {
                DynamicContextHolder.poll();
                logger.debug("clean datasource");
            }
        }
    }
    

    到这一步就已完成了,然后把DataSource注解加到service的类或方法上,即可实现操作指定的多数据源。

    三、通过接口的入参来指定数据源

    上面的注解DataSource的value是写死在代码里面的,但是我们有这样的需求,前端根据接口入参来操作指定数据源的数据。

    所以我们在上面的基础上,再改造一下
    再写两个注解

    /**
     * 多数据源注解-注解数据源的dbKey
     *
     * @author ZhangXinLin
     */
    @Target({ElementType.PARAMETER})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface DbKey {
    }
    

    这个自定义注解DbKey,是作用在参数上的,标志该参数是用来指定数据源的dbKey

    /**
     * 多数据源注解
     *
     * @author ZhangXinLin
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    public @interface DynamicDataSource {
    
    }
    
    

    DynamicDataSource的自定义注解是用在controller的方法上

    DynamicDataSource注解的切面类

    这个切面类是根据方法的入参dbKey来动态切换数据源,核心代码是调用这行代码
    //根据dbKey动态设置数据源
    DynamicDataSource.setDataSource(dbKey);

    /**
     * @Description: 动态加载多数据源(启动后加载)
     **/
    @Aspect
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class DynamicDataSourceAspect {
        protected Logger logger = LoggerFactory.getLogger(getClass());
    
        @Pointcut("@annotation(io.renren.datasource.annotation.DynamicDataSource) " +
                "|| @within(io.renren.datasource.annotation.DynamicDataSource)")
        public void dynamicdataSourcePointCut() {
    
        }
    
        @Around("dynamicdataSourcePointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            //获取参数,根据参数获取数据源
            String dbKey = getDbKey(point);
    
            if( dbKey != null){
                //根据dbKey动态设置数据源
                DynamicDataSource.setDataSource(dbKey);
            }
            try {
                return point.proceed();
            } finally {
                DynamicContextHolder.poll();
                logger.debug("clean datasource");
            }
        }
    
    
        /**
         * 根据@DbKey注解获取数据源的dbKey
         * @param point
         * @return
         */
        private String getDbKey(ProceedingJoinPoint point) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Method method = signature.getMethod();
            Object[] args = point.getArgs();
            String value = null;
            //参数注解,1维是参数,2维是注解
            Annotation[][] annotations = method.getParameterAnnotations();
            for (int i = 0; i < annotations.length; i++) {
                Object param = args[i];
                Annotation[] paramAnn = annotations[i];
                //参数为空,直接下一个参数
                if (param == null || paramAnn.length == 0) {
                    continue;
                }
                for (Annotation pAnnotation : paramAnn) {
                    if (pAnnotation.annotationType().equals(DbKey.class)) {
                        value =  param.toString();
                        break;
                    }
                }
            }
            return value;
        }
    }
    

    然后在controller的方法上加上注解@DynamicDataSource,以及入参加上注解@Dbkey

        /**
         * 查看数据源的所有表列表
         * @param id
         * @return
         */
        @DynamicDataSource
        @RequestMapping("/getTableList/{id}")
        public R getTableList(@PathVariable("id") @DbKey Integer id){
            List<Map<String, Object>> list = datasourceService.queryTableList(id);
            return R.ok().put("list", list);
        }
    

    看看实际效果

    1. 首先我们表的数据源的增删改写一个页面
      在这里插入图片描述

    在这里插入图片描述

    1. 在页面调用getTableList接口来切换数据源的数据

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

    在这里插入图片描述
    可以看到,前端页面选择不同的数据库,后端接口就会根据dbKey的入参来动态切换数据源,从而查询出不同数据源的表名列表



    源码

    源码在一个还没有写完的快速开发平台的项目里面(功能可以在线编写模版,线上配置数据源,不用改代码就可以编写开发模版,生成不同系统的基础代码);
    这个项目还没写完,后面写完也会开源出来,所以这里的源码暂时没有

    展开全文
  • 那为什么会有这篇文章呢,那是因为小编在使用mybatis-plus这个框架,明明一个多数据源配置很简单,但是网上其他博客着实太过于复杂了。 从数据源配置,每一个数据源配置一个config类,还有需要自己实现AOP的,当时...

    一、引言

    在我们实际业务开发中,难以避免需要同时涉及多个数据库,可能一个API中所需要的数据,往往是包含了多个数据库中的数据,

    这个时候我们就需要在项目运行中,切换数据源。 那为什么会有这篇文章呢,那是因为小编在使用mybatis-plus这个框架,明明一个多数据源配置很简单,但是网上其他博客着实太过于复杂了。

    从数据源配置、每一个数据源配置一个config类、配置枚举类、还有需要自己实现AOP的,当时小编就在想mybatis-plus是为简化而生,怎么搞这么复杂。

    接下来不要眨眼,很快就实现了。

    二、具体实现

    步骤一:我们先需要引入maven配置,这里小编是使用3.1.0版本,可自选。dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。

     <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>3.1.0</version>
            </dependency>

    步骤二:多数据源配置,需要注意的后面都有写注释。

    spring:
      datasource:
        dynamic:
          primary: master #设置默认的数据源或者数据源组,默认值即为master
          strict: false #设置严格模式,默认false不启动. 启动后在未匹配到指定数据源时候会抛出异常,不启动则使用默认数据源.
          datasource:
            master:
              url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
            slave_1:
              url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
            slave_2:
              url: ENC(xxxxx) # 内置加密,使用请查看详细文档
              username: ENC(xxxxx)
              password: ENC(xxxxx)
              driver-class-name: com.mysql.jdbc.Driver
              schema: db/schema.sql # 配置则生效,自动初始化表结构
              data: db/data.sql # 配置则生效,自动初始化数据
              continue-on-error: true # 默认true,初始化失败是否继续
              separator: ";" # sql默认分号分隔符

    步骤三:步骤三就可以直接使用啦,真的是太方便了,@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解

    /**
     * 数据源切换测试
     *
     * @author IT 贱男
     * description:
     * date: 2021/2/25 18:01
     */
    @Service
    @AllArgsConstructor
    public class TestJdbcServiceImpl implements TestJdbcService {
    
        // 数据库1的mapper
        private final KkOrderMapper kkOrderMapper;
    
        // 数据库2的mapper
        private final SysUserMapper sysUserMapper;
    
        // 不写DS默认是主数据源
        @Override
        public void testMySql1() {
            List<KkOrder> kkOrders = kkOrderMapper.selectList(null);
            System.out.println(kkOrders.size());
        }
    
        // 注解里面填写多数据源配置的名字
        @DS("slave_1")
        @Override
        public void testMysql2() {
            List<SysUser> sysUsers = sysUserMapper.selectList(null);
            System.out.println(sysUsers.size());
        }
    }

     

    三、官方建议以及特性

    建议

    1. 本框架只做 切换数据源 这件核心的事情,并不限制你的具体操作,切换了数据源可以做任何CRUD。
    2. 配置文件所有以下划线 _ 分割的数据源 首部 即为组的名称,相同组名称的数据源会放在一个组下。
    3. 切换数据源可以是组名,也可以是具体数据源名称。组名则切换时采用负载均衡算法切换。
    4. 默认的数据源名称为 master ,你可以通过 spring.datasource.dynamic.primary 修改。
    5. 方法上的注解优先于类上注解。
    6. 强烈建议只在service的类和方法上添加注解,不建议在mapper上添加注解

    特性

    1. 支持 数据源分组 ,适用于多种场景 纯粹多库 读写分离 一主多从 混合模式。
    2. 支持数据库敏感配置信息 加密 ENC()。
    3. 支持每个数据库独立初始化表结构schema和数据库database。
    4. 支持 自定义注解 ,需继承DS(3.2.0+)。
    5. 提供对Druid,Mybatis-Plus,P6sy,Jndi的快速集成。
    6. 简化Druid和HikariCp配置,提供 全局参数配置 。配置一次,全局通用。
    7. 提供 自定义数据源来源 方案。
    8. 提供项目启动后 动态增加移除数据源 方案。
    9. 提供Mybatis环境下的 纯读写分离 方案。
    10. 提供使用 spel动态参数 解析数据源方案。内置spel,session,header,支持自定义。
    11. 支持 多层数据源嵌套切换 。(ServiceA >>> ServiceB >>> ServiceC)。
    12. 提供对shiro,sharding-jdbc,quartz等第三方库集成的方案,注意事项和示例。
    13. 提供 基于seata的分布式事务方案。 附:不支持原生spring事务。
    14. 提供 本地多数据源事务方案。 附:不支持原生spring事务

     

     

     

     

     

     

     

    展开全文
  • --主从配置依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.2.1</version>

    pom.xml

    <!--主从配置依赖-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
                <version>3.2.1</version>
            </dependency>
    

    application-dev.yml

    datasource:
        dynamic:
          primary: master #设置默认的数据源或者数据源组,默认值即为master
          datasource:
            master:
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
              url: xxxx
            slave_1:
              username: scott
              password: scott
              driver-class-name: oracle.jdbc.driver.OracleDriver
              url: xxxx
    

    默认数据源:master

    @DS(value = “数据源名称”)
    注解在类上或方法上来切换数据源

    测试代码:

    @Test
    public void testIworker() {
    System.out.println(testMapper .selectList(null));
    }
    

    mapper类:

    @Mapper
    @DS("slave_1")
    public interface testMapper extends BaseMapper <User > {
    }
    
    展开全文
  • 主要任务:多数据源配置Hikari + MybatisPlus 项目结构 注意要点 两个数据源的mapperScan目录一定要分开,否则就会出现数据源1中的表去数据源2数据库中查询的情况 原因:在mapperScan的过程中会把扫描到的类归属于...
  • springboot+Mybatis 多数据源配置

    千次阅读 2021-12-03 09:03:36
    以下以mysql、sqlServer两种数据源为例做演示配置(请以自身情况做相应配置) 1.所需依赖包引入 <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-...
  • characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&rewriteBatchedStatements=true username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver db2: # 数据源2配置 url:...
  • 编写application.yml,如:项目路径名及端口,mybatisplus配置,mysql多数据源配置。 新建DataSourceContextHolder 用于设置,获取,清空 当前线程内的数据源变量。 新建 MultipleDataSource 实现 ...
  • 默认配置文件只有application.yml文件,但是为了多数据源配置这里新增application-dev.yml配置文件 配置文件appliction.yml。里面对application-dev.yml文件进行了引用,对项目名称、端口以及druid进行了相关配置 ...
  • 在使用springboot开发业务应用程序的过程中,使用多数据源的场景很常见。目前,业界常用的spring boot整合多数据源的方案主要有两个,一个是Mapper分包方式,另一个是AOP切片方式。针对这种多数据源使用场景的痛点,...
  • springboot+mybatisplus+druid配置mysql与clickhouse多数据源配置 1、导入jar包 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId>...
  • 比如需要查询的student和teacher信息分别来自于mysql和postgresql,或者都是用mysql但jdbc连接串不同,这个时候在application.properties中需要配置多数据源,相应地需要多个配置类、多个mapper映射。 连接池配置...
  • SpringBoot里做多数据源配置,可以直接使用dynamic-datasource提供的服务,简单便捷 一、POM里加入依赖包 <!--数据源依赖--> <dependency> <groupId>com.baomidou</groupId> <...
  • 苞米豆多数据配置在pom.xml使用 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.22</version&
  • 在实际开发中,经常可能遇到在一个应用中可能需要访问个数据库的情况,在需要切换数据源Service或Mapper方法上添加@DataSource注解@DataSource(value = DataSourceType.MASTER),其中value用来表示数据源名称 ...
  • 配置类: 注意 @Primary 注解,他可以理解为优先选择,这下面的代码是一个主要连接,如果需要再加入一个连接的话复制以下代码修改name 吧@Primary注解去掉就行了。 注意spring.datasource.test 是后面配置的。 @...
  • 配置了两个数据源,查询Dao却报错表不存在。 (1)maven <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <...
  • 多数据源配置JdbcTemplate(十五)

    千次阅读 2021-09-02 20:14:34
    主要内容: 1. JdbcTemplate的多数据源配置 2. 多数据源配置 DataSource 3. 多数据源配置 JdbcTemplate
  • 配置多数据源,由于不常用就进行了百度,百度了很久(有很多都不能用),加上自己修修改改,终于能用了。那么就废话不多说,直接上代码。 SpringBoot应用启动类注解: @MapperScan("com.xxx.mapper") @SpringBoot...
  • 多数据源配置MyBatisPlus(十八)

    千次阅读 2021-09-09 09:42:14
    主要内容: 1. 多数据源MyBatisPlus的配置 2. dynamic-datasource-spring-boot-starter 的使用
  • Boot项目原配Mysql数据库 。业务需要加上sqlserver数据库 网上看了两天 ...1.首先 依赖配置按需配置重点是数据库jar包跟bootjar包。版本无需考虑com.alibabadruid1.1.14mysqlmysql-connector-javaruntimeorg.jfreej...
  • python配置多数据源

    2021-11-21 14:54:42
    # 多数据源配置 DATABASES_APPS_MAPPING = { 'demoapp': 'shujuku1', 'myapp': 'default', } DATABASE_ROUTERS = ['application.db_router.DatabaseRouter'] # (引号很重要) 具体详解: DATABASES_APPS_MAPPING中的...
  • } } 四、动态切换 可以根据传入参数(配置文件配置的db1,db2,db3)动态切换数据源 @DS("#dataBaseName")//使用spel从参数获取 public List selectSpelByKey(String dataBaseName) { return userMapper.selectUsers()...
  • 目录一、pom依赖二、yml配置文件三、自定义切换数据源注解四、定义AOP切面切换数据源五、读取Druid连接池配置六、Druid多数据源配置类七、配置动态数据源八、操作数据源类九、数据源类型枚举类总结 流程解释: 一、...
  • 记得面试时候,有面试官会问道,你们多数据源是怎么实现的呀。.......,一阵蒙蔽中,然后说道我们之前项目中,没有用到多数据源。所幸,目前做得项目中有一个业务逻辑中,用到个数据库数据情况,多数据源华丽上线...
  • 前段时间开发时,遇到了需要同时消费两个不同数据源的RabbitMQ的情况,试遍了很方法,找到了一个相对比较合适的方式,在此做一个简单的分享。 实战 1、yml配置 # 原数据源 v1: spring: rabbitmq: host: 192....
  • springboot Jpa多数据源(不同库)配置

    千次阅读 2021-08-13 11:34:27
    springboot版本不同对多数据源配置代码有一定影响,部分方法和配置略有不同。 本文采用的springboot版本为2.3.12,数据源为mysql和postgresql 二、配置实战 2.1 基础pom <dependencies> <dependency> ...
  • MySQL和SQL server两个数据源配置使用 场景: 一个MySQL8数据源,一个SQL server数据源 MySQL数据库操作使用Jpa框架,SQL server部分使用JdbcTemplate 配置文件application.yml 需要注意:只有一个MySQL数据源时,...
  • shardingsphere+dynamic+mybatisPlugs配置多数据源简介特性约定使用方式1. 导包2. yml配置3. 配置类 简介 dynamic-datasource-spring-boot-starter ????是一个基于springboot的快速集成多数据源的启动器 github: ...
  • SpringBoot配置多数据源

    2021-12-14 15:43:16
    jar包: mybatis-plus-boot-starter mysql-connector-java druid-spring-boot-starter dynamic-datasource-spring-boot-starter yml配置文件: server: port: 8080 ... primary: db1 # 配置默认... db1: # 数据源1配置

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,085,979
精华内容 434,391
关键字:

多数据源配置