• spring配置双数据源


    系列文章目录

    第一章 spring框架构成
    第二章 spring容器
    第三章 spring配置bean
    第四章 bean的继承和依赖
    第五章 bean的生命周期
    第六章 spring依赖注入
    第七章 Spring AOP
    第八章 spring事务
    第九章 spring事件监听
    第十章 web应用使用spring
    第十一章 spring注解整理
    第十二章 spring事务失效场景
    第十三章 spring bean的作用域
    第十四章 spring的扩展接口
    第十五章 spring引入外部属性文件
    第十六章 spring自定义属性编辑器
    第十七章 spring配置双数据源



    spring配置双数据源

    前段时间有个需求,需要将将数据存到两个数据库中,一个库中存放主信息,一个库中存放特殊信息,看来是要使用双数据源了,来搞起来吧

    既然是双数据源,先不管怎么切换,配置得先搞起来

    数据源配置

    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    
        
        <property name="initialSize" value="5"/>
        
        <property name="maxActive" value="10"/>
        
        <property name="minIdle" value="1"/>
        
        <property name="maxWait" value="3000"/>
    
        <property name="poolPreparedStatements" value="true"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="33"/>
    
        
        <property name="validationQuery" value="SELECT 1"/>
        
        <property name="testOnBorrow" value="false"/>
        
        <property name="testOnReturn" value="false"/>
        
        <property name="testWhileIdle" value="true"/>
    
        
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        
        <property name="minEvictableIdleTimeMillis" value="300000"/>
    
        
        <property name="removeAbandoned" value="false"/>
        
    bean>
    
    
    <bean name="adDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${adurl}"/>
        <property name="username" value="${adusername}"/>
        <property name="password" value="${adpassword}"/>
    
        
        <property name="initialSize" value="5"/>
        
        <property name="maxActive" value="10"/>
        
        <property name="minIdle" value="1"/>
        
        <property name="maxWait" value="3000"/>
    
        <property name="poolPreparedStatements" value="true"/>
        <property name="maxPoolPreparedStatementPerConnectionSize" value="33"/>
    
        
        <property name="validationQuery" value="SELECT 1"/>
        
        <property name="testOnBorrow" value="false"/>
        
        <property name="testOnReturn" value="false"/>
        
        <property name="testWhileIdle" value="true"/>
    
        
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        
        <property name="minEvictableIdleTimeMillis" value="300000"/>
    
        
        <property name="removeAbandoned" value="false"/>
        
    bean>
    
    
        <bean id="dynamicDataSource" class="com.zhanghe.webconfig.datasource.DynamicDataSource">
            <property name="targetDataSources">
                <map key-type="java.lang.String">
                    <entry key="video" value-ref="dataSource"/>
                    <entry key="ad" value-ref="adDataSource"/>
                map>
            property>
            
            <property name="defaultTargetDataSource" ref="dataSource"/>
        bean>
    

    动态数据源

    在这里插入图片描述

    配置好了数据源之后,需要进行定义动态数据源,继承AbstractRoutingDataSource,AbstractRoutingDataSource是基于特定的查找key路由到特定的数据源。它内部维护了一组目标数据源,并且做了路由key与目标数据源之间的映射,提供基于key查找数据源的方法。

    public class DynamicDataSource extends AbstractRoutingDataSource {
        private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
    
        @Override
        protected Object determineCurrentLookupKey() {
          	// 获取当前数据源key
            String key = DataSourceHolder.getCurDataSource();
            LOGGER.info("{}线程 获取到的数据源key--->{}",Thread.currentThread().getName(),key);
    				// 如果没有的话,则使用默认的key
            if(StringUtils.isBlank(key)){
                key = DataSourceHolder.getDefaultDataSource();
            }
            LOGGER.info("{}线程 数据源选择--->{}",Thread.currentThread().getName(),key);
            return key;
        }
    }
    
    // 存储当前数据源的key
    public class DataSourceHolder {
        private static final ThreadLocal<String> CUR_DATA_SOURCE = new ThreadLocal<>();
    
        private static final String DEFAULT_DATA_SOURCE = "video";
    
        public static String getCurDataSource(){
            return CUR_DATA_SOURCE.get();
        }
    
        public static String getDefaultDataSource(){
            return DEFAULT_DATA_SOURCE;
        }
    
        public static void setCurDataSource(String dataSource){
            if(StringUtils.isNotBlank(dataSource)){
                CUR_DATA_SOURCE.set(dataSource);
            }
        }
    
        public static void clearDataSource(){
            CUR_DATA_SOURCE.remove();
        }
    }
    

    配置是都搞定了,那么怎么切换呢,可以看到在动态数据源中其实是根据key来进行路由获取数据源的,那么其实就是怎么改变这个key了,而且是动态改变,那么就用spring aop来进行解决吧

    数据源切换

    首先定义一个注解@DataSource,来标识当前方法要使用的数据源

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

    然后来进行aop的逻辑来根据注解的name属性来存储key

    @Order(1)  // 这里要注意一下,由于spring中的@Transactional也是使用的aop来开启事务的,而切换数据源要在开启事务之前,所以我将@Order设置为了1
    @Aspect
    @Component
    public class DataSourceAspect {
        private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);
    
        @Pointcut(value="@annotation(com.zhanghe.webconfig.datasource.DataSource)")
        public void pointcut(){
    
        }
    
        @Before(value = "pointcut()")
        public void before(JoinPoint joinPoint){
            String name = getDataSourceName(joinPoint);
            LOGGER.info("{}线程拦截切换数据源{}",Thread.currentThread().getName(),name);
            DataSourceHolder.setCurDataSource(name);
        }
    
        @After(value = "pointcut()")
        public void after(){
            DataSourceHolder.clearDataSource();
        }
    
        /**
         * 获取数据源lookupkey
         * @param joinPoint
         * @return
         */
        public String getDataSourceName(JoinPoint joinPoint) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            if (method != null) {
                DataSource dataSource = method.getAnnotation(DataSource.class);
    
                return dataSource.name();
            } else {
                return null;
            }
        }
    }
    

    这就大功告成了,双数据源的配置就搞定了

    参考文献

  • 相关阅读:
    总结:Springboot工作记录
    selenium报错解决
    【Excel】单元格如何设置可选项、固定表头
    Windows下安装RabbitMQ
    Uncaught ReferenceError: $ is not defined-- SpringMCV
    844. Backspace String Compare
    艺术与科技的狂欢,阿那亚2022砂之盒沉浸艺术季
    含文档+PPT+源码等]精品基于SSM的奶茶店信息管理系统[包运行成功]程序设计源码计算机毕设
    Kubernetes集群+Keepalived+Nginx+防火墙 实例
    关于I/O——内存与CPU与磁盘之间的关系
  • 原文地址:https://blog.csdn.net/Lxn2zh/article/details/126953168