美文网首页
java超快速入门(七):动态数据库切换

java超快速入门(七):动态数据库切换

作者: 自我De救赎 | 来源:发表于2021-10-04 10:13 被阅读0次

    有的时候我们的代码同时会用到多个数据库,因此需要在请求mysql前,切换数据源
    通过aop+注解的方式能轻松实现多数据源
    这里介绍使用spring+aspectj自动代理+注解的方式实现mybatis多数据源的切换

    知识点

    • 数据源路由器AbstractRoutingDataSource,继承该类的数据源,每次请求时都会通过determineCurrentLookupKey方法决定使用的数据源
    • @annotation切入点表达式

    多数据源

    首先我们需要创建多个链接池(数据源)

    /** DynamicDataSourceProperties.java **/
    //配置类
    
    package com.sinbxeunha.josechan.dynamicDataSource.config;
    
    import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    
    /**
     * 数据源配置,这里继承了springboot的单数据源配置类,并且为配置分配了key
     */
    @Component
    @Primary
    @ConfigurationProperties("datasource.dynamic.properties")
    public class DynamicDataSourceProperties extends DataSourceProperties {
    
        private String key;
    
        public String getKey() {
            return key;
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    }
    
    /** DynamicDataSourceRegisterProperties.java **/
    package com.sinbxeunha.josechan.dynamicDataSource.config;
    
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
    
    import java.util.List;
    
    /**
     * 动态多数据源配置文件
     */
    @Component
    @ConfigurationProperties("datasource.dynamic.register")
    public class DynamicDataSourceRegisterProperties {
    
        public String defaultKey;
    
        public List<DynamicDataSourceProperties> properties;
    
        public List<DynamicDataSourceProperties> getProperties() {
            return properties;
        }
    
        public void setProperties(List<DynamicDataSourceProperties> properties) {
            this.properties = properties;
        }
    
        public String getDefaultKey() {
            return defaultKey;
        }
    
        public void setDefaultKey(String defaultKey) {
            this.defaultKey = defaultKey;
        }
    }
    
    # application.properties
    # 多数据源配置
    # 第一个数据源
    datasource.dynamic.register.properties[0].key=xss
    datasource.dynamic.register.properties[0].url=jdbc:mysql://127.0.0.1:3306/xss
    datasource.dynamic.register.properties[0].username=root
    datasource.dynamic.register.properties[0].password=**********
    datasource.dynamic.register.properties[0].driver-class-name=com.mysql.cj.jdbc.Driver
    # 第二个数据源
    datasource.dynamic.register.properties[1].key=forecast
    datasource.dynamic.register.properties[1].url=jdbc:mysql://127.0.0.1:3306/forecast
    datasource.dynamic.register.properties[1].username=root
    datasource.dynamic.register.properties[1].password=**********
    datasource.dynamic.register.properties[1].driver-class-name=com.mysql.cj.jdbc.Driver
    
    /** DynamicDataSourceContextHolder.java **/
    package com.sinbxeunha.josechan.dynamicDataSource.config;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 用来存储数据源的key
     */
    public class DynamicDataSourceContextHolder {
    
        private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
    
        /**
         * 存储已经注册的数据源的key,所有配置的数据源
         */
        public static List<String> dataSourceIds = new ArrayList<>();
    
        /**
         * 线程级别的私有变量,当前线程的数据源key
         */
        private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
    
        public static String getDataSourceRouterKey () {
            return HOLDER.get();
        }
    
        public static void setDataSourceRouterKey (String dataSourceRouterKey) {
            logger.info("切换至{}数据源", dataSourceRouterKey);
            HOLDER.set(dataSourceRouterKey);
        }
    
        /**
         * 设置数据源之前一定要先移除
         */
        public static void removeDataSourceRouterKey () {
            HOLDER.remove();
        }
    
        /**
         * 判断指定DataSrouce当前是否存在
         *
         * @param dataSourceId 数据源ID
         * @return 是否存在Datasource
         */
        public static boolean containsDataSource(String dataSourceId){
            return dataSourceIds.contains(dataSourceId);
        }
    }
    
    /** DynamicRoutingDataSource.java **/
    package com.sinbxeunha.josechan.dynamicDataSource.register;
    
    import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceContextHolder;
    import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceProperties;
    import com.sinbxeunha.josechan.dynamicDataSource.config.DynamicDataSourceRegisterProperties;
    import com.zaxxer.hikari.HikariDataSource;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.boot.context.properties.bind.Bindable;
    import org.springframework.boot.context.properties.bind.Binder;
    import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
    import org.springframework.boot.context.properties.source.ConfigurationPropertyNameAliases;
    import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
    import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
    import org.springframework.boot.jdbc.DataSourceBuilder;
    import org.springframework.context.EnvironmentAware;
    import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
    import org.springframework.core.env.Environment;
    import org.springframework.core.type.AnnotationMetadata;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 动态数据源注册
     * 实现 ImportBeanDefinitionRegistrar 实现数据源注册
     * 实现 EnvironmentAware 用于读取application.yml配置
     */
    @Component
    public class DynamicDataSourceRegister implements InitializingBean {
    
        /** 自动注入配置类 */
        @Autowired
        DynamicDataSourceRegisterProperties dynamicDataSourceRegisterProperties;
    
        private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class);
    
    
        /**
         * 别名
         */
        private final static ConfigurationPropertyNameAliases aliases = new ConfigurationPropertyNameAliases();
    
        static {
            aliases.addAliases("url", "jdbc-url");
            aliases.addAliases("username", "user");
        }
    
        /**
         * 存储我们注册的数据源
         */
        private final Map<Object, Object> customDataSources = new HashMap<Object, Object>();
    
        /** 记录默认数据源 */
        private DataSource defaultDatasource = null;
    
        /**
         * 继承自InitializingBean,对象生成后自动执行
         * @throws Exception 异常
         */
        @Override
        public void afterPropertiesSet() throws Exception {
            List<DynamicDataSourceProperties> properties = dynamicDataSourceRegisterProperties.getProperties();
            String defaultKey = dynamicDataSourceRegisterProperties.getDefaultKey();
            for (DynamicDataSourceProperties property : properties){
                DataSourceBuilder<?> builder = property.initializeDataSourceBuilder();
                DataSource dataSource = builder.type(property.getType()).build();
                customDataSources.put(property.getKey(), dataSource);
                DynamicDataSourceContextHolder.dataSourceIds.add(property.getKey());
    
                if(property.getKey().equals(defaultKey)){
                    defaultDatasource = dataSource;
                }
            }
        }
    
        public Map<Object, Object> getCustomDataSources() {
            return customDataSources;
        }
    
        public DataSource getDefaultDatasource() {
            return defaultDatasource;
        }
    }
    
    /** DynamicRoutingDataSource.java **/
    package com.sinbxeunha.josechan.dynamicDataSource.config;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    /**
     * 动态数据源类,继承了AbstractRoutingDataSource,实现其中的determineCurrentLookupKey
     * 该方法会在每次查询前被调用,用于获取数据源
     */
    public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
    
        private static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
    
        @Override
        protected Object determineCurrentLookupKey() {
            String dataSourceName = DynamicDataSourceContextHolder.getDataSourceRouterKey();
            logger.info("当前数据源是:{}", dataSourceName);
            return DynamicDataSourceContextHolder.getDataSourceRouterKey();
        }
    }
    

    相关文章

      网友评论

          本文标题:java超快速入门(七):动态数据库切换

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