wxh努力中 2021-05-24 10:35 采纳率: 53.8%
浏览 244
已结题

达梦 数据库与mysql 多数据源整合

数据源 可自由切换 最好有demo参考 后期可能会加入 es 等

  • 写回答

2条回答 默认 最新

  • 大黄瓜de冬天 2021-05-24 11:18
    关注

    第一步:手动清除线程变量副本,调用clearDataSourceType方法

    package com.cplink.framework.datasource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 数据源切换处理
     * 
     * @author ruoyi
     */
    public class DynamicDataSourceContextHolder
    {
        public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
    
        /**
         * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
         *  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
         */
        private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
    
        /**
         * 设置数据源的变量
         */
        public static void setDataSourceType(String dsType)
        {
            log.info("切换到{}数据源", dsType);
            CONTEXT_HOLDER.set(dsType);
        }
    
        /**
         * 获得数据源的变量
         */
        public static String getDataSourceType()
        {
            return CONTEXT_HOLDER.get();
        }
    
        /**
         * 清空数据源变量
         */
        public static void clearDataSourceType()
        {
            CONTEXT_HOLDER.remove();
        }
    }
    

    第二步:准备数据源连接信息,包括:数据源名称(任取,不重名即可)、用户名、密码、url、数据库驱动(由数据库库类型代码转换,写mysql即可)

    第三步:写一个继承AbstractRoutingDataSource的类,并连接数据源连接

    package com.cplink.framework.datasource;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.stat.DruidDataSourceStatManager;
    import com.cplink.common.constant.Constants;
    import com.cplink.common.utils.StringUtils;
    import com.cplink.framework.config.properties.DruidProperties;
    import com.cplink.framework.web.domain.DataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    /**
     * 动态数据源
     * 
     * @author ruoyi
     */
    public class DynamicDataSource extends AbstractRoutingDataSource
    {
        private final Log log = LogFactory.getLog(DynamicDataSource.class);
        private Map<Object, Object> dynamicTargetDataSources;
    
        @Autowired
        private DruidProperties druidProperties;
    
        public DynamicDataSource(javax.sql.DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources)
        {
            super.setDefaultTargetDataSource(defaultTargetDataSource);
            super.setTargetDataSources(targetDataSources);
            super.afterPropertiesSet();
        }
    
        @Override
        protected Object determineCurrentLookupKey()
        {
            return DynamicDataSourceContextHolder.getDataSourceType();
        }
    
    
        @Override
        public void setTargetDataSources(Map<Object, Object> targetDataSources) {
            super.setTargetDataSources(targetDataSources);
            this.dynamicTargetDataSources = targetDataSources;
        }
    
    
        /**
         * 检查并创建数据源(数据源存在不需要在创建)
         * @param dataSource DataBaseSource实体类信息
         * @param boo 是否默认数据目标源配置(true:系统默认连接信息,false自定义连接信息)
         * @param targetDataSources 当boo为false时用此源扩展信息
         * @throws Exception
         */
        public void createDataSourceWithCheck(DataSource dataSource, boolean boo, DruidDataSource targetDataSources) throws Exception {
            if(StringUtils.isNull(dataSource) || StringUtils.isEmpty(dataSource.getDataSourceId())){
                log.info("数据源基本配置信息不能为空");
                throw new Exception("数据源基本配置信息不能为空");
            }
            String datasourceId = dataSource.getDataSourceId();
            log.info("正在检查数据源:"+datasourceId);
            if(StringUtils.isEmpty(this.dynamicTargetDataSources))
                this.dynamicTargetDataSources = new HashMap<Object, Object>();
            Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
            //不存在'datasourceId'数据源,新建数据源
            if (!dynamicTargetDataSources2.containsKey(datasourceId)) {
                createDataSource(dataSource,boo,targetDataSources);
                return;
            }
            log.info("数据源"+datasourceId+"之前已经创建,准备测试数据源是否正常...");
            DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(datasourceId);
            boolean rightFlag = true;
            Connection connection = null;
            try {
                log.info("准备获取数据库连接...");
                connection = druidDataSource.getConnection();
                log.info("数据源"+datasourceId+"正常");
            } catch (Exception e) {
                log.error(e.getMessage(),e); //把异常信息打印到日志文件
                rightFlag = false;
                log.info("缓存数据源"+datasourceId+"已失效,准备删除...");
                if(delDatasources(datasourceId)) {
                    log.info("缓存数据源删除成功");
                } else {
                    log.info("缓存数据源删除失败");
                }
            } finally {
                if(null != connection) {
                    connection.close();
                }
            }
            if(rightFlag) {
                log.info("不需要重新创建数据源");
                return;
            }
            log.info("准备重新创建数据源...");
            createDataSource(dataSource,boo,targetDataSources);
            log.info("重新创建数据源完成");
        }
    
        /**
         * 创建数据源
         * @param dataSource
         * @throws Exception
         */
        public void createDataSource(DataSource dataSource, boolean boo, DruidDataSource targetDataSources) throws Exception {
            if(StringUtils.isNull(dataSource) || StringUtils.isEmpty(dataSource.getDataSourceId())){
                log.info("数据源基本配置信息不能为空");
                throw new Exception("数据源基本配置信息不能为空");
            }
            String datasourceId = dataSource.getDataSourceId();
            log.info("准备创建数据源"+datasourceId);
            testParamBoo(dataSource);
            if(!testDatasource(dataSource.getUserName(),dataSource.getPassWord(),dataSource.getUrl(),dataSource.getDataBaseType())) {
                log.error("数据源配置有错误");
                throw new Exception("数据源配置有错误");
            }
            boolean result = this.initDataSource(dataSource,boo,targetDataSources);
            if(!result) {
                log.error("数据源"+datasourceId+"配置正确,但是创建失败");
                throw new Exception("数据源"+datasourceId+"配置正确,但是创建失败");
            }
        }
    
        /**
         * 初始化数据源
         * @param dataBaseSource
         * @param boo 是否默认数据目标源配置(true:系统默认连接信息,false自定义连接信息)
         * @param targetDataSources 当boo为false时此源必填
         * @return
         */
        public boolean initDataSource(DataSource dataBaseSource, boolean boo, DruidDataSource targetDataSources)throws Exception {
            if(StringUtils.isNull(dataBaseSource) || StringUtils.isEmpty(dataBaseSource.getDataSourceId())){
                log.info("数据源基本配置信息不能为空");
                throw new Exception("数据源基本配置信息不能为空");
            }
            String key = dataBaseSource.getDataSourceId();
            DruidDataSource druidDataSource = null;
            if(!boo){
                //自定义数据源信息
                druidDataSource = getBaseDataSource(targetDataSources,dataBaseSource);
            }else{
                //默认数据源信息(除基本信息外,如用户名、密码、源名称、url连接信息、驱动)
                DruidDataSource druidData = new DruidDataSource();
                DruidDataSource dataSource = druidProperties.dataSource(druidData);
                druidDataSource = getBaseDataSource(dataSource,dataBaseSource);
            }
            if(StringUtils.isNull(druidDataSource)){
                log.error("数据源驱动配置错误");
                return false;
            }
            try {
                //数据源配置初始化
                druidDataSource.init();
            } catch (Exception e) {
                log.error("数据源配置初始化错误:".concat(e + ""));
                return false;
            }
            if(StringUtils.isEmpty(this.dynamicTargetDataSources))
                this.dynamicTargetDataSources = new HashMap<Object, Object>();
            this.dynamicTargetDataSources.put(key, druidDataSource);
            setTargetDataSources(this.dynamicTargetDataSources);// 将map赋值给父类的TargetDataSources
            super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
            log.info(key.concat("数据源初始化成功"));
            return true;
        }
    
        /**
         * 删除数据源
         * @param datasourceid
         * @return
         */
        public boolean delDatasources(String datasourceid) {
            if(StringUtils.isEmpty(this.dynamicTargetDataSources))
                this.dynamicTargetDataSources = new HashMap<Object, Object>();
            Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
            if (!dynamicTargetDataSources2.containsKey(datasourceid))
                return false;
            Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
            for (DruidDataSource l : druidDataSourceInstances) {
                if (datasourceid.equals(l.getName())) {
                    dynamicTargetDataSources2.remove(datasourceid);
                    DruidDataSourceStatManager.removeDataSource(l);
                    setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
                    super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 测试数据源配置
         * @param userName 用户名
         * @param passWord 密码
         * @param url url
         * @param type 数据库类型
         * @return
         */
        public boolean testDatasource(String userName, String passWord, String url, String type){
            String driveClass = getDriveClass(type);
            if(Constants.FAIL.equals(driveClass))
                return false;
            try { // 排除连接不上的错误
                Class.forName(driveClass);
                DriverManager.getConnection(url, userName, passWord);// 相当于连接数据库
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    
        /**
         * 根据类型转换数据库驱动
         * @param databasetype
         * @return
         */
        private String getDriveClass(String databasetype){
            if("mysql".equalsIgnoreCase(databasetype))
                return Constants.MYSQL_DRIVER;
            if("oracle".equalsIgnoreCase(databasetype))
                return Constants.ORACLE_DRIVER;
            if("sqlserver".equalsIgnoreCase(databasetype))
                return Constants.SQLSERVER_DRIVER;
            return Constants.FAIL;
        }
    
    
        /**
         * 封装数据源连接信息
         * @param druidDataSource 自定义的连接信息或者默认的本系统连接信息
         * @param dataSource 基本的数据源信息(包含数据驱动、数据源key、url连接信息、用户名、密码)
         * @return
         */
        private DruidDataSource getBaseDataSource(DruidDataSource druidDataSource, DataSource dataSource)throws Exception{
            testParamBoo(dataSource);
            druidDataSource.setName(dataSource.getDataSourceId());
            String driveClass = getDriveClass(dataSource.getDataBaseType());
            if(Constants.FAIL.equals(driveClass))
                return null;
            druidDataSource.setDriverClassName(driveClass);
            druidDataSource.setUrl(dataSource.getUrl());
            druidDataSource.setUsername(dataSource.getUserName());
            druidDataSource.setPassword(dataSource.getPassWord());
            return druidDataSource;
        }
    
        private void testParamBoo(DataSource dataSource)throws Exception{
            if(StringUtils.isNull(dataSource) || StringUtils.isEmpty(dataSource.getDataSourceId())||StringUtils.isEmpty(dataSource.getDataBaseType())|| StringUtils.isEmpty(dataSource.getUserName())|| StringUtils.isEmpty(dataSource.getPassWord())|| StringUtils.isEmpty(dataSource.getUrl())){
                log.info("数据源基本配置信息不能为空");
                throw new Exception("数据源基本配置信息不能为空");
            }
        }
    }

    第五步:切换数据源

    DynamicDataSourceContextHolder.clearDataSourceType();
    
    String key = "test1";
    String username = "test1";
    String password = "test1";
    String url = "";
    String dataType = "mysql";
    
    DataSource dataBaseSource = new DataSource();
    dataBaseSource.setDataSourceId(key);
    dataBaseSource.setUrl(url);
    dataBaseSource.setDataBaseType(dataType);
    dataBaseSource.setUserName(username);
    dataBaseSource.setPassWord(password);
    
    try{
                //创建数据源连接&检查 若存在则不需重新创建
               dynamicDataSource.createDataSourceWithCheck(dataBaseSource,true,null);
                //切换到该数据源
                DynamicDataSourceContextHolder.setDataSourceType(key);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
    //下面写执行切换了数据源之后的表的操作
    
    

    另外DruidProperties类

    package com.cplink.framework.config.properties;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    import com.alibaba.druid.pool.DruidDataSource;
    
    /**
     * druid 配置属性
     * 
     * @author ruoyi
     */
    @Configuration
    public class DruidProperties
    {
        @Value("${spring.datasource.druid.initialSize}")
        private int initialSize;
    
        @Value("${spring.datasource.druid.minIdle}")
        private int minIdle;
    
        @Value("${spring.datasource.druid.maxActive}")
        private int maxActive;
    
        @Value("${spring.datasource.druid.maxWait}")
        private int maxWait;
    
        @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")
        private int timeBetweenEvictionRunsMillis;
    
        @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")
        private int minEvictableIdleTimeMillis;
    
        @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}")
        private int maxEvictableIdleTimeMillis;
    
        @Value("${spring.datasource.druid.validationQuery}")
        private String validationQuery;
    
        @Value("${spring.datasource.druid.testWhileIdle}")
        private boolean testWhileIdle;
    
        @Value("${spring.datasource.druid.testOnBorrow}")
        private boolean testOnBorrow;
    
        @Value("${spring.datasource.druid.testOnReturn}")
        private boolean testOnReturn;
    
        public DruidDataSource dataSource(DruidDataSource datasource)
        {
            /** 配置初始化大小、最小、最大 */
            datasource.setInitialSize(initialSize);
            datasource.setMaxActive(maxActive);
            datasource.setMinIdle(minIdle);
    
            /** 配置获取连接等待超时的时间 */
            datasource.setMaxWait(maxWait);
    
            /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */
            datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
    
            /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */
            datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
    
            /**
             * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
             */
            datasource.setValidationQuery(validationQuery);
            /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */
            datasource.setTestWhileIdle(testWhileIdle);
            /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
            datasource.setTestOnBorrow(testOnBorrow);
            /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */
            datasource.setTestOnReturn(testOnReturn);
            return datasource;
        }
    }
    

    基本就这些,事实上需要做的就是创建一个管理连接线程副本,用多数据源的信息测试连接,成功就初始化,在切换数据源,就可以在相应的数据源上进行表的操作。

     

    评论

报告相同问题?

悬赏问题

  • ¥50 微信聊天记录备份到电脑提示成功了,但还是没同步到电脑微信
  • ¥15 python怎么在已有视频文件后添加新帧
  • ¥20 虚幻UE引擎如何让多个同一个蓝图的NPC执行一样的动画,
  • ¥15 fluent里模拟降膜反应的UDF编写
  • ¥15 MYSQL 多表拼接link
  • ¥15 关于某款2.13寸墨水屏的问题
  • ¥15 obsidian的中文层级自动编号
  • ¥15 同一个网口一个电脑连接有网,另一个电脑连接没网
  • ¥15 神经网络模型一直不能上GPU
  • ¥15 pyqt怎么把滑块和输入框相互绑定,求解决!