精华内容
下载资源
问答
  • 数据源为static
    千次阅读
    2022-02-07 14:33:13

    1.yml多数据源配置

    amdb,josdb,josdbqy,分别为自定义数据源名称,type指定数据源使用的连接池。

    2.新建类DataSourceProperties用于读取yml文件中的自定义数据源属性

    @Component
    public class DataSourceProperties {
        @Value("${spring.datasource.amdb.jdbc-url}")
        private String amdbUrl;
        @Value("${spring.datasource.amdb.username}")
        private String amdbUserName;
        @Value("${spring.datasource.amdb.password}")
        private String amdbPassword;
        @Value("${spring.datasource.amdb.driver-class-name}")
        private String amdbDriverClass;
    
        @Value("${spring.datasource.josdb.jdbc-url}")
        private String josdbUrl;
        @Value("${spring.datasource.josdb.username}")
        private String josdbUserName;
        @Value("${spring.datasource.josdb.password}")
        private String josdbPassword;
        @Value("${spring.datasource.josdb.driver-class-name}")
        private String josdbDriverClass;
    
    
        @Value("${spring.datasource.josdbqy.jdbc-url}")
        private String josdbqyUrl;
        @Value("${spring.datasource.josdbqy.username}")
        private String josdbqyUserName;
        @Value("${spring.datasource.josdbqy.password}")
        private String josdbqyPassword;
        @Value("${spring.datasource.josdbqy.driver-class-name}")
        private String josdbqyDriverClass;
    
        public String getAmdbUrl() {
            return amdbUrl;
        }
    
        public void setAmdbUrl(String amdbUrl) {
            this.amdbUrl = amdbUrl;
        }
    
        public String getAmdbUserName() {
            return amdbUserName;
        }
    
        public void setAmdbUserName(String amdbUserName) {
            this.amdbUserName = amdbUserName;
        }
    
        public String getAmdbPassword() {
            return amdbPassword;
        }
    
        public void setAmdbPassword(String amdbPassword) {
            this.amdbPassword = amdbPassword;
        }
    
        public String getAmdbDriverClass() {
            return amdbDriverClass;
        }
    
        public void setAmdbDriverClass(String amdbDriverClass) {
            this.amdbDriverClass = amdbDriverClass;
        }
    
        public String getJosdbUrl() {
            return josdbUrl;
        }
    
        public void setJosdbUrl(String josdbUrl) {
            this.josdbUrl = josdbUrl;
        }
    
        public String getJosdbUserName() {
            return josdbUserName;
        }
    
        public void setJosdbUserName(String josdbUserName) {
            this.josdbUserName = josdbUserName;
        }
    
        public String getJosdbPassword() {
            return josdbPassword;
        }
    
        public void setJosdbPassword(String josdbPassword) {
            this.josdbPassword = josdbPassword;
        }
    
        public String getJosdbDriverClass() {
            return josdbDriverClass;
        }
    
        public void setJosdbDriverClass(String josdbDriverClass) {
            this.josdbDriverClass = josdbDriverClass;
        }
    
        public String getJosdbqyUrl() {
            return josdbqyUrl;
        }
    
        public void setJosdbqyUrl(String josdbqyUrl) {
            this.josdbqyUrl = josdbqyUrl;
        }
    
        public String getJosdbqyUserName() {
            return josdbqyUserName;
        }
    
        public void setJosdbqyUserName(String josdbqyUserName) {
            this.josdbqyUserName = josdbqyUserName;
        }
    
        public String getJosdbqyPassword() {
            return josdbqyPassword;
        }
    
        public void setJosdbqyPassword(String josdbqyPassword) {
            this.josdbqyPassword = josdbqyPassword;
        }
    
        public String getJosdbqyDriverClass() {
            return josdbqyDriverClass;
        }
    
        public void setJosdbqyDriverClass(String josdbqyDriverClass) {
            this.josdbqyDriverClass = josdbqyDriverClass;
        }
    }
    

    3.创建类DynamicDataSource继承AbstractRoutingDataSource并实determineCurrentLookupKey()

    方法,从DataSourceContextHolder动态获取对应线程的数据源。

    public class DynamicDataSource extends AbstractRoutingDataSource {
    
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return DataSourceContextHolder.getDataSource();
    	}
    
    }

     

    4.再创建DataSourceConfig类,创建数据源

    
    @Configuration
    public class DataSourceConfig {
    
        @Autowired
        DataSourceProperties dataSourceProperties;
    
        @Bean(name = "amdb")
        public DataSource amdb() {
            HikariDataSource amdb = new HikariDataSource();
            amdb.setJdbcUrl(dataSourceProperties.getAmdbUrl());
            amdb.setDriverClassName(dataSourceProperties.getAmdbDriverClass());
            amdb.setUsername(dataSourceProperties.getAmdbUserName());
            amdb.setPassword(dataSourceProperties.getAmdbPassword());
            amdb.setPoolName("HikariPool-amdb");
            amdb.setAutoCommit(true);
            amdb.setReadOnly(false);
            amdb.setConnectionTestQuery("SELECT 1;");
            return amdb;
        }
    
        @Bean(name = "josdb")
        public DataSource josdb() {
            HikariDataSource josdb = new HikariDataSource();
            josdb.setJdbcUrl(dataSourceProperties.getJosdbUrl());
            josdb.setDriverClassName(dataSourceProperties.getJosdbDriverClass());
            josdb.setUsername(dataSourceProperties.getJosdbUserName());
            josdb.setPassword(dataSourceProperties.getJosdbPassword());
            josdb.setPoolName("HikariPool-josdb");
            josdb.setAutoCommit(true);
            josdb.setReadOnly(false);
            josdb.setConnectionTestQuery("SELECT 1;");
            return josdb;
        }
    
        @Bean(name = "josdbqy")
        public DataSource josdbqy() {
            HikariDataSource josdbqy = new HikariDataSource();
            josdbqy.setJdbcUrl(dataSourceProperties.getJosdbqyUrl());
            josdbqy.setDriverClassName(dataSourceProperties.getJosdbqyDriverClass());
            josdbqy.setUsername(dataSourceProperties.getJosdbqyUserName());
            josdbqy.setPassword(dataSourceProperties.getJosdbqyPassword());
            josdbqy.setPoolName("HikariPool-josdbqy");
            josdbqy.setAutoCommit(true);
            josdbqy.setReadOnly(false);
            josdbqy.setConnectionTestQuery("SELECT 1;");
            return josdbqy;
        }
    
        @Bean(name = "amdbJdbcTemplate")
        public JdbcTemplate amdbJdbcTemplate(
                @Qualifier("amdb") DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
    
        @Bean(name = "josdbJdbcTemplate")
        public JdbcTemplate josdbJdbcTemplate(
                @Qualifier("josdb") DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
    
        @Bean(name = "josdbqyJdbcTemplate")
        public JdbcTemplate josdbqyJdbcTemplate(
                @Qualifier("josdbqy") DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }
    
        @Primary
        @Bean("dynamicDataSource")
        public DataSource dynamicDataSource() {
            Map<Object, Object> targetDataSources = new HashMap<>(3);
            targetDataSources.put(DataSourceTypeEnum.amdb, amdb());
            targetDataSources.put(DataSourceTypeEnum.josdb, josdb());
            targetDataSources.put(DataSourceTypeEnum.josdbqy, josdbqy());
            // 添加数据源名称到列表
            DataSourceContextHolder.dataSourceIds.add(DataSourceTypeEnum.amdb.name());
            DataSourceContextHolder.dataSourceIds.add(DataSourceTypeEnum.josdb.name());
            DataSourceContextHolder.dataSourceIds.add(DataSourceTypeEnum.josdbqy.name());
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            // 如果没有指定数据源自动切换主数据源
            dynamicDataSource.setDefaultTargetDataSource(amdb());
            dynamicDataSource.setTargetDataSources(targetDataSources);
            return dynamicDataSource;
        }
    
    
        @Bean
        public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
            JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(entityManagerFactory);
            jpaTransactionManager.setDataSource(dynamicDataSource());
            return jpaTransactionManager;
        }

    创建对应的三个数据源,amdb,josdb,josdbqy,再创建对应的jdbctemplate, 再创建指定数据源的方法dynamicDataSource(),以及最后指定对应的Jpa事务管理器。

    5.创建DataSourceContextHolder动态数据源上下文管理

    public class DataSourceContextHolder {
    	// 存放当前线程使用的数据源类型
    	private static final ThreadLocal<DataSourceTypeEnum> contextHolder = new ThreadLocal<>();
    
    	 //存放数据源id
        public static List<String> dataSourceIds = new ArrayList<String>();
    
    
    	// 设置数据源
    	public static void setDataSource(DataSourceTypeEnum type) {
    		contextHolder.set(type);
    	}
    
    	// 获取数据源
    	public static DataSourceTypeEnum getDataSource() {
    		return contextHolder.get();
    	}
    
    	// 清除数据源
    	public static void clearDataSource() {
    		contextHolder.remove();
    	}
    
    	//判断当前数据源是否存在
        public static boolean isContainsDataSource(String dataSourceId) {
            return dataSourceIds.contains(dataSourceId);
        }
    }

    6.创建自定义注解DS类

    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.METHOD })
    public @interface DS {
    	DataSourceTypeEnum value() default DataSourceTypeEnum.amdb;
    }
    

    7.使用aop的切面来切换数据源。

    @Aspect
    @Order(-10) // 保证该AOP在@Transactional之前执行
    @Component
    public class DynamicDataSourceAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
    
        @Before(value = "@annotation(source)")
        public void changeDataSource(JoinPoint point, DS source) throws Exception {
            DataSourceTypeEnum currentSource = source.value();
            logger.info("Change DataSource To:[" + currentSource + "]");
            DataSourceContextHolder.setDataSource(currentSource);
        }
    
        @After(value = "@annotation(source)")
        public void restoreDataSource(JoinPoint point, DS source) {
            // 方法执行完毕之后,销毁当前数据源信息,进行垃圾回收。
            DataSourceContextHolder.clearDataSource();
            logger.info("Clear Change DataSource...");
        }
    
    }

    8.在service的实现类来使用数据源的切换,在所需方法上加上DS注解以及对应的数据源类型,即可实现数据源切换

     9.加上枚举类

    public enum DataSourceTypeEnum {
    	amdb, josdb, josdbqy
    }

     

     

     

     

    更多相关内容
  • [文件数据]osFM Static 1.1_osfilemanager_static_1.1.zip源码PHP项目代码下载[文件数据]osFM Static 1.1_osfilemanager_static_1.1.zip源码PHP项目代码下载 1.适合个人搭建网站项目参考 2.适合学生毕业设计搭建...
  • 上两遍已经描述了动态多数据源的原理和基础实现了,上面两篇都是从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的入参来动态切换数据源,从而查询出不同数据源的表名列表



    源码

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

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

    千次阅读 2022-05-17 10:58:55
    在实际的开发或者线上环境中,一般都不仅仅是一个数据库走天下,而是根据业务进行拆分多个数据库,今天就来学习如何对springboot进行多数据源配置。

    作者平台:

    | CSDN:blog.csdn.net/qq_41153943

    | 掘金:juejin.cn/user/651387…

    | 知乎:www.zhihu.com/people/1024…

    | GitHub:github.com/JiangXia-10…

    | 微信公众号:1024笔记

    本文大约6171字,预计阅读时长16分钟

    前言

    在实际的开发或者线上环境中,一般都不仅仅是一个数据库走天下,而是根据业务进行拆分多个数据库,今天就来学习如何对springboot进行多数据源配置。

    本文的工程基础是之前的项目工程,具体可以参考SpringBoot整合Redis使用教程。项目源码最后也会同步只github。地址在最后,欢迎下载star!

    正文

    数据库

    首先准备下数据库:这里有两个数据库,一个是test数据库,里面有个user表,数据如下:

    /*
    Source Server         : testdb
    Source Host           : localhost:3306
    Source Database       : test
    Target Server Type    : MYSQL
    Date: 2021-12-09 10:51:21
    */
    DROP TABLE IF EXISTS `user`;
    CREATE TABLE `user` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
      `email` varchar(255) NOT NULL COMMENT '邮箱',
      `password` varchar(255) NOT NULL COMMENT '密码',
      `username` varchar(255) NOT NULL COMMENT '姓名',
      PRIMARY KEY (`id`),
      UNIQUE KEY `email` (`email`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    INSERT INTO `user` VALUES ('1', 'liqing@lol.com', '123456', '李青');
    INSERT INTO `user` VALUES ('2', 'daome@lol.com', '234567', '刀妹');
    INSERT INTO `user` VALUES ('3', 'yasuo@lol.com', '345678', '亚索');
    
    复制代码
    

    接着有个spring_cache数据库,并且里面有个department表,数据如下:

    /*
    Source Server         : testdb
    Source Server Version : 50527
    Source Host           : localhost:3306
    Source Database       : spring_cache
    Date: 2021-12-09 11:32:16
    */
    
    SET FOREIGN_KEY_CHECKS=0;
    DROP TABLE IF EXISTS `department`;
    CREATE TABLE `department` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `departmentName` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Records of department
    -- ----------------------------
    INSERT INTO `department` VALUES ('1', '中路部门');
    INSERT INTO `department` VALUES ('2', '打野部门');
    INSERT INTO `department` VALUES ('3', '上路部门');
    复制代码
    

    代码

    首先需要在application.yml配置文件中配置两个数据源配置,分别为db1,b2,具体配置如下:

    spring:
      application:
        name: share
      datasource:
        dynamic:
          primary: db1 # 配置默认数据库
        db1:
          jdbc-url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Chongqing&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&verifyServerCertificate=false&autoReconnct=true&autoReconnectForPools=true&allowMultiQueries=true
          username: root
          password: jiang
          driver-class-name: com.mysql.cj.jdbc.Driver
        db2:
          jdbc-url: jdbc:mysql://localhost:3306/spring_cache?serverTimezone=Asia/Chongqing&useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&useSSL=false&verifyServerCertificate=false&autoReconnct=true&autoReconnectForPools=true&allowMultiQueries=true
          username: root
          password: jiang
          driver-class-name: com.mysql.cj.jdbc.Driver
    复制代码
    

    因为这里采用的是之前工程的代码,所以关于另一个数据源相关的代码这里不贴了,这里主要写新增数据源的代码逻辑,之前的可以的参考之前的文章,或者github下载源码,地址贴在最后。

    在bean文件夹下声明一个department实体类,如下:

    public class Department {
        public Integer id;
        public String departmentName;
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getDepartmentName() {
            return departmentName;
        }
    
        public void setDepartmentName(String departmentName) {
            this.departmentName = departmentName;
        }
    
        public Department(Integer id, String departmentName) {
            this.id = id;
            this.departmentName = departmentName;
        }
    
        @Override
        public String toString() {
            return "Department{" +
                    "id=" + id +
                    ", departmentName='" + departmentName + '\'' +
                    '}';
        }
    }
    复制代码
    

    然后新增一个dao文件:

    public interface DepartmentDao {
        /**
         * 根据查询数据
         *
         */
        @Select("select id,departmentName from department where id=#{id}")
        Department findById(@Param("id") int id);
    }
    复制代码
    

    service和serviceimpl如下:

    public interface DepartmentService {
        //根据id查询部门
        Department findByID(int id);
    }
    复制代码
    
    @Service
    public class DepartmentServiceImpl implements DepartmentService {
        @Autowired
        DepartmentDao departmentDao;
        @Override
        public Department findByID(int id) {
            return departmentDao.findById(id);
        }
    }
    复制代码
    

    然后就是controller:

    @RestController
    @RequestMapping(value = "/do/department")
    public class DepartmentController {
        @Autowired
        DepartmentService departmentService;
    
        //根据用户名查询数据
        @RequestMapping(value = "/dep", method = RequestMethod.GET)
        public Department department(@RequestParam(value = "id",required = true) int id){
            return departmentService.findByID(id);
        }
    }
    复制代码
    

    最后写一个关于数据源的配置文件,具体如下:

    @Configuration
    @MapperScan(basePackages = "com.springboot.springbootdemo.dao.db1",sqlSessionFactoryRef = "db1SqlSessionFactory")
    public class DB1DataSourceConfig {
        static final String MAPPER_LOCATION = "classpath:/mapping/db1/*.xml";
    
        @Bean("db1DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.db1")
        public DataSource getDb1DataSource(){
            return DataSourceBuilder.create().build();
        }
    
        @Bean("db1SqlSessionFactory")
        public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
            return bean.getObject();
        }
        @Bean("db1SqlSessionTemplate")
        public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }
    复制代码
    
    @Configuration
    @MapperScan(basePackages ="com.springboot.springbootdemo.dao.db2",sqlSessionFactoryRef = "db2SqlSessionFactory")
    public class DB2DataSourceConfig {
        static final String MAPPER_LOCATION = "classpath:/mapping/db2/*.xml";
    
        @Bean("db2DataSource")
        @ConfigurationProperties(prefix = "spring.datasource.db2")
        public DataSource getDb1DataSource(){
            return DataSourceBuilder.create().build();
        }
    
        @Bean("db2SqlSessionFactory")
        public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(dataSource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATION));
            return bean.getObject();
        }
    
        @Bean("db2SqlSessionTemplate")
        public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    }
    复制代码
    

    测试

    以上步骤完毕之后进行测试,启动项目成功之后,首先在地址栏输入:

    http://localhost:8081/share/do/user/userAll
    复制代码
    

    查询db1数据库的数据,结果如下:

    图片

    接着查询db2数据库的数据,地址栏输入:

    http://localhost:8081/share/do/department/dep?id=1
    复制代码
    

    结果如下:

    图片

    可见通过以上配置,能够实现springboot的多数据源配置效果。

    总结

    springboot配置多数据源其实很简单,首先在application配置文件中配置多个数据源的配置,然后有几个数据源就写几个数据源的配置类即可。本项目的工程结构如下:

    图片

    最后resource文件夹下的mapping文件下的*.Mapper.xml,可以没有具体的逻辑,但是结构文件得有,不然会报错。在数据源的配置类中的MAPPER_LOCATION有使用到!

    如有任何问题或者不对的地方欢迎一起交流讨论学习!

    本项目源码在:

    github.com/JiangXia-10…

    欢迎下载、Star!

    相关推荐

    展开全文
  • SpringBoot+自定义注解实现多数据源配置
    😊 @ 作者: 一恍过去
    🎊 @ 社区: Java技术栈交流
    🎉 @ 主题: SpringBoot+自定义注解实现多数据源配置
    ⏱️ @ 创作时间: 2022年06月15日

    前言

    通过自定义注解实现不同数据源直接的切换,不使用注解时默认使用主库数据源,在需要使用指定数据源的在调用Mapper的方法上加上自定义注解即可;

    1、创建数据库(表)

    分别在mysql中创建两个库:systemlog,并且创建对应的表(根据项目实际情况的定),如下:

    在这里插入图片描述

    2、创建自定义注解

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataSource {
    
        /**
         * 数据源名称
         * @return
         */
        String name();
    }
    

    3、配置yaml

    spring:
      application:
        name: quartz
      datasource:
        # 数据源一
        system:
          driverClassName: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://127.0.0.1:3307/system?useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
          username: root
          password: lhzlx
        # 数据源二
        log:
          driverClassName: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://127.0.0.1:3307/log?useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
          username: root
          password: lhzlx
    
        # hikari连接池
        type: com.zaxxer.hikari.HikariDataSource
        hikari:
          #最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值
          maximum-pool-size: 10
          #最小空闲连接,默认值 10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
          minimum-idle: 2
          #连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒
          connection-timeout: 60000
          #空闲连接超时时间,默认值600000ms(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;
          #不等于0且小于10秒,会被重置为10秒。
          #只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放(自动释放过期链接)
          idle-timeout: 600000
          #连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
          max-lifetime: 640000
          #连接测试查询
          connection-test-query: SELECT 1
    
    
    #mapper 别名扫描
    mybatis:
      mapper-locations: classpath*:mappers/*.xml
      type-aliases-package: com.lhz.demo.model.entity
      #数据库类型
      configuration.database-id: mysql
      #自动驼峰转换
      configuration.map-underscore-to-camel-case: true
    

    注意: 在使用hikari作为连接池时,如果配置多数据源需要将url修改为jdbc-url,使用druid作为连接池则不需要修改;否则会出现jdbcUrl is required with driverClassName错误;

    4、DataSourceHolder

    public class DataSourceHolder {
        /**
         * 使用ThreadLocal储存上下游的数据源名称
         */
        private static final ThreadLocal<String> sourceName = new ThreadLocal<>();
    
        /**
         * 得到当前的数据库连接
         * @return
         */
        public static String  getDataSource() {
            return sourceName.get();
        }
    
        /**
         * 设置当前的数据库连接
         * @param name
         */
        public static void setDataSource(String name) {
            sourceName.set(name);
        }
    
        /**
         * 清除当前的数据库连接
         */
        public static void clearDataSource() {
            sourceName.remove();
        }
    
    }
    

    4、创建RoutingDataSource

    public class RoutingDataSource extends AbstractRoutingDataSource {
    
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceHolder.getDataSource();
        }
    
    }
    

    5、数据源名称枚举定义

    public enum DataSourceEnum {
        /**
         * 数据源名称,对应yaml中的数据源名称
         */
        SYSTEM("system","系统默认数据源"),
        LOG("log","日志数据源");
    
        DataSourceEnum(String name, String desc) {
            this.name = name;
            this.desc = desc;
        }
    
        /**
         * 数据源名称
         */
        private final String name;
    
        /**
         * 描述
         */
        private final String desc;
    
        public String getName() {
            return name;
        }
    
        public String getDesc() {
            return desc;
        }
    }
    

    6、创建DataSourceAspect

    @Aspect
    @Component
    public class DataSourceAspect {
        @Pointcut("@annotation(com.lhz.demo.annotation.DataSource)")
        public void dataSource() {}
    
        @Before("dataSource()")
        public void before(JoinPoint joinPoint) {
            MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
            Method method = methodSignature.getMethod();
            try {
                // 添加默认数据源
                String dataSource = DataSourceEnum.SYSTEM.getName();
    
                boolean present = method.isAnnotationPresent(DataSource.class);
                if(present){
                    DataSource annotation = method.getAnnotation(DataSource.class);
                    dataSource = annotation.name();
                }
    
                // 此处添加线程对应的数据源到上下文
                // 在AbstractRoutingDataSource子类中拿到数据源, 加载后进行配置
                DataSourceHolder.setDataSource(dataSource);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        /**
         * 清除数据源, 方法执行完成后, 清除数据源
         */
        @After("dataSource()")
        public void after(JoinPoint joinPoint) {
            DataSourceHolder.clearDataSource();
        }
    }
    

    7、数据源配置

    
    @Configuration
    public class DataSourceConfig {
        /**
         * yml中配置的数据源名称
         * @return
         */
        @Bean(name =  "system")
        @ConfigurationProperties(prefix = "spring.datasource.system")
        public DataSource system() {
            //database1数据源
            return DataSourceBuilder.create().build();
        }
    
        @Bean(name = "log")
        @ConfigurationProperties(prefix = "spring.datasource.log")
        public DataSource log() {
            //database2数据源
            return DataSourceBuilder.create().build();
        }
    
        /**
         * 指定主数据源
         * @return
         */
        @Bean(name="routingDataSource")
        @Primary
        public DataSource dataSource() {
            RoutingDataSource routingDataSource = new RoutingDataSource();
            // 设置默认数据源
            routingDataSource.setDefaultTargetDataSource(system());
            // 将数据源写入ThreadLocal
            Map<Object, Object> dataSourceMap = new HashMap<>(4);
            dataSourceMap.put(DataSourceEnum.SYSTEM.getName(), system());
            dataSourceMap.put(DataSourceEnum.LOG.getName(), log());
            routingDataSource.setTargetDataSources(dataSourceMap);
            return routingDataSource;
    
        }
        @Bean
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dataSource());
        }
    }
    

    8、Mapper接口

    com.lhz.demo.mapper目录下创建LogMapperSysMapper
    LogMapper:

    @Mapper
    public interface LogMapper {
        List<TbLog> selectAll();
    }
    

    SysMapper:

    @Mapper
    public interface SysMapper {
        List<TbSystem> selectAll();
    }
    
    

    9、Mappeing(XML)

    resources/mappers目录下创建LogMapper.xmlSystemMapper.xml文件

    LogMapper.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lhz.demo.mapper.LogMapper">
    
        <select id="selectAll" resultType="com.lhz.demo.model.entity.TbLog">
            select * from tb_log
        </select>
    </mapper>
    

    SystemMapper.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.lhz.demo.mapper.SysMapper">
    
    
        <select id="selectAll" resultType="com.lhz.demo.model.entity.TbSystem">
            select * from tb_system
        </select>
    </mapper>
    

    10、Service

    @Service
    public class TestService {
    
        @Resource
        private SysMapper sysMapper;
    
        @Resource
        private LogMapper logMapper;
    
        /**
         * 不指定数据源,模式使用system数据源
         * @return
         */
        public Object sys() {
            return sysMapper.selectAll();
        }
    
    
        /**
         * 指定数据源查询,该注解也可以放在Mapper接口上,表示整个Mapper接口都使用指定数据源
         * @return
         */
        @DataSource(name = "log")
        public Object log() {
            return logMapper.selectAll();
        }
    }
    

    11、Controller

    @RestController
    @RequestMapping("/test")
    @Slf4j
    public class TestController {
    
        @Resource
        private TestService testService;
    
        /**
         * 查询system库
         * @return
         */
        @GetMapping("/sys")
        public Object sys() {
            return testService.sys();
        }
    
    
        /**
         * 查询log库
         * @return
         */
        @GetMapping("/log")
        public Object log() {
            return testService.log();
        }
    }
    

    12、配置启动类

    Application启动类需要屏蔽DataSource自动配置,修改如下:

    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication .class, args);
        }
    }
    

    13、目录结构

    在这里插入图片描述

    14、测试

    启动项目分别访问`http://localhost:9090/test/sys`与`http://localhost:9090/test/log`均有数据输出,则表示配置成功;
    

    在这里插入图片描述

    在这里插入图片描述

    展开全文
  • spring mvc 配置多数据源

    千次阅读 2022-04-14 11:12:58
    程序是使用的是spring JdbcTemplate架构,简要记录下配置多数据源的方法: 1,设置数据源: 在application.properties文件中配置多个数据源,示例中我配置了2个数据源,如下: A、oracle jdbc.driver=oracle....
  • 什么是数据源?如何配置数据源

    千次阅读 2021-12-23 17:03:06
    1.什么是数据源数据源是连接到数据库的一类路径,它包含了访问数据库的信息(地址、用户名、密码)。 拓展: 数据库 数据库是一个容器,包含了很多数据,当然这些数据可能存在不同的小容器(表)里面。 若用水来形容...
  • 创建动态数据源2.把动态数据源植入MyBatis3. 如何使用 前言 项目背景:我们做的是一个分布式数据库运维平台,项目会配置自己的数据源,同时因为是数据库运维平台,可以动态接入新的数据库集群,需要到这些集群节点...
  • 很久之前写一篇静态的springboot...之前说的静态多数据源是每个数据源配置一套mapper,现在说说如何动态多数据源共用一套mapper,以及数据源不从applition.yml中配置,实现从数据库中读取数据源配置并动态切换数据源
  • SpringBoot多数据源

    千次阅读 2022-02-07 15:25:34
    ​ 定义:在项目中针对一个数据库都其建立一套独立的数据处理逻辑,包括数据源(DataSource),会话工厂(SqlSessionFactory),连接,DAO操作。 一、搭建springboot工程 ​ 项目工程结构如下所示,主要从实体...
  • java切换数据源(多数据源

    千次阅读 2022-04-06 14:46:27
    现在要在一个服务里设置两个数据源:oracle和mysql 需要写datasource文件
  • springboot+mybatis-plus配置多数据源的方式网上有...但是数据库配置时,需要先加载一个主数据源,读取数据库表,把表里面配置数据库源再加载bean。 2.实现步骤 1.由于在MyBatisPlusConfig中配置的地方需要配置一个Da
  • SpringBoot静态数据源指的是将多个数据源信息配置在配置文件中,在项目启动时加载配置文件中的多个数据源,并实例化多个数据源Bean,再通过分包/Aop达到切换数据源的目的 如果想要新增或者修改数据源,必须修改配置文件,...
  • SpringBoot 实现动态数据源

    千次阅读 2022-05-12 23:02:05
    SpringBoot 实现动态数据源 功能: 前端请求接口时携带用户信息,后端拦截器获取用户信息后切换数据源查询数据。 使用场景:多租户,sass,pass等项目。 实现原理:主要通过SpringBoot提供的...
  • Springboot多数据源配置

    千次阅读 2020-09-07 13:49:13
    对于SpringMVC,多数据源是通过aop去实现。Springboot也大同小异。 1.配置依赖 除了常规的springboot依赖之外,再加上aop,数据连接池等依赖。 <properties> <java.version>1.8</java.version>...
  • Spring Boot, Spring Cloud项目,变单数据源数据源配置问题
  • springboot动态切换数据源

    千次阅读 2021-12-09 23:55:18
    在springboot项目中只需一句代码即可实现多个数据源之间的切换: // 切换sqlserver数据源: DataSourceContextHolder.setDataBaseType(DataSourceEnum.SQLSERVER_DATASOURCE); … // 切换mysql数据源 ...
  • Spring-动态数据源

    千次阅读 2022-02-26 18:17:45
    Spring-动态数据源 动态数据源的原理得先说清。 原理 平常在使用Mysql的时候是通过JDBC的,得给一个url,userName,和password,如下: jdbc:mysql://localhost:3306/t_db1?useSSL=false&...
  • 本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。...
  • spring boot+mybatis plus 实现动态数据源

    千次阅读 2022-07-20 20:28:54
    @Async(ThreadPoolConfig.THREAD_POOL),确保切换数据源之后另一个线程。由于项目开发中设计到游戏运营平台的搭建,游戏中每一个不同的区服使用的是不同的数据库存储数据。区服1包括game_1_data(游戏数据数据库),...
  • 首先Springboot+Mybatis+druid动态多数据源的配置是这样的 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @MapperScan(basePackages = "com.xxx.xxx.mapper") @Import({...
  • druid多数据源配置

    千次阅读 2022-03-28 19:18:21
    druid多数据源配置
  • 文引出了Flink程序自定义数据源的方法,我们来再次回顾下,并自定义数据源类 从Mysql中加载数据源
  • springboot+es(二)多数据源配置

    千次阅读 2020-02-24 17:23:59
    在config 文件夹下新增数据源配置,有几个数据源就新增几个配置类,例子是老师信息数据源 和学生信息数据源两个 。 引入依赖 <dependency> <groupId>org.elasticsearch.client...
  • Springboot动态切换数据源

    千次阅读 2021-11-30 10:31:31
    动态切换数据源 在springboot项目中只需一句代码即可实现多个数据源之间的切换: // 切换sqlserver数据源: DataSourceContextHolder.setDataBaseType(DataSourceEnum.SQLSERVER_DATASOURCE); ...... // 切换mysql数据...
  • 之前配置多数据源踩了很多坑,包括事务注解失效,多数据源无法切换等,特此把配置过程整理下来,以供后来的小伙伴参考。
  • springMVC多数据源配置

    千次阅读 2019-06-18 11:28:51
    配置2个数据源,一个orcale一个mysql例 #oracle jdbc.oracle.driver=oracle.jdbc.OracleDriver jdbc.oracle.url=jdbc:oracle:thin:@localhost:1521:xe jdbc.oracle.user=root jdbc.oracle.password=root ...
  • SpringBoot 多数据源切换

    千次阅读 2022-03-25 14:40:06
    SpringBoot 多数据源切换 抽象类AbstractRoutingDataSource,通过扩展这个类实现根据不同的请求切换数据源。 通过determineCurrentLookupKey()方法获取一个key, 通过key从resolvedDataSources中获取数据源...
  • MyBatisPlus--多数据源

    千次阅读 2022-04-21 21:02:36
    支持 数据源分组 ,适用于多种场景、纯粹多库、读写分离、一主多从、混合模式。 目前来模拟一个纯粹多库的一个场景,其他场景类似;场景说明: 创建两个库,分别:mybatis_plus与mybatis_plus_1,使每个库一张表...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 559,709
精华内容 223,883
热门标签
关键字:

数据源为static