• 使用Spring的AbstractRoutingDataSource实现多数据源动态切换


    1、配置多个数据源

    <bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource">
         <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver">
         </property>
         <property name="url" value="jdbc:jtds:sqlserver://127.0.0.1;databaseName=test">
         </property>
         <property name="username" value="***"></property>
         <property name="password" value="***"></property>
     </bean>
     <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
         <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver">
         </property>
         <property name="url" value="jdbc:jtds:sqlserver://127.0.0.2:1433;databaseName=test">
         </property>
         <property name="username" value="***"></property>
         <property name="password" value="***"></property>
    </bean>

    2、定义一个类继承AbstractRoutingDataSource实现determineCurrentLookupKey方法,该方法可以实现数据库的动态切换;由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。

    package com.sgl.dataSource;
    import
    org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    public class DynamicDataSource extends AbstractRoutingDataSource{
    
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceHolder.getDataSourceHolder();
        }
    
    }

    3、定义一个可以设置当前线程的变量的工具类,用于设置对应的数据源名称:

    package com.sgl.dataSource;
    /**
     * 可以设置当前线程的变量的工具类,用于设置对应的数据源名称
     * @author 尐蘇
     *
     */
    public class DynamicDataSourceHolder {
        /**
         * 当前线程
         */
        private static ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>();
        /**
         * 设置数据源名称
         * @param dataSourceType
         */
        public static void setDataSourceHolder(String dataSourceName){
            dataSourceHolder.set(dataSourceName);
        }
        /**
         * 获取数据源名称
         * @return
         */
        public static String getDataSourceHolder(){
            return dataSourceHolder.get();
        }
        /**
         * 清除数据源名称
         */
        public static void clearDataSourceHolder(){
            dataSourceHolder.remove();
        }
    }

    4、spring多数据源配置

    <!-- 编写spring 配置文件的配置多数源映射关系 -->
    <bean class="com.sgl.dataSource.DynamicDataSource" id="dataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry value-ref="dataSource1" key="dataSource1" />
                <entry value-ref="dataSource2" key="dataSource2" />
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="dataSource1">
        </property>
    </bean> 

    5、如果没有数据库的事务管理,已经可以实现数据库的动态切换了。但是如果涉及到数据库的事务管理,需要在数据库事务开启切换数据库,否则数据库的切换只能在下次数据库操作时才生效。可以定义一个aop处理类在数据库事务开启之前切换数据库:

    package com.sgl.dataSource;
    
    import java.lang.reflect.Method;
    
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.aop.MethodBeforeAdvice;
    /**
     * aop处理类,在数据库事务开启之前切换数据库
     * @author 尐蘇
     *
     */
    public class DataSourceAspect implements MethodBeforeAdvice,AfterReturningAdvice{
    
        @Override
        public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
            DynamicDataSourceHolder.clearDataSourceHolder();
        }
    
        @Override
        public void before(Method method, Object[] arg1, Object arg2) throws Throwable {
            
            if (method.isAnnotationPresent(DataSource.class)) {
                DataSource dataSource = method.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSourceHolder(dataSource.value());
            }else{
                //设置成默认的数据源
                DynamicDataSourceHolder.setDataSourceHolder("dataSource");
            }
            
        }
    
    }

    或者:

    package com.sgl.dataSource;
    
    import java.lang.reflect.Method;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.Signature;
    
    /**
     * 实现数据源切换的类
     * @author 尐蘇
     *
     */
    public class DataSourceExchange {
        /**
         * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
         * @param joinPoint
         * @throws Exception
         */
        public void beforeDaoMethod(JoinPoint joinPoint)throws Exception{
            Class<?> target = joinPoint.getTarget().getClass();//获取目标方法所在的类
            Signature signature = joinPoint.getSignature();
            Class<?>[] interfaces = target.getInterfaces();//确定此对象所表示的类或接口实现的接口
            for (Class<?> clazz : interfaces) {
                
            }
        }
        /**
         * 提取目标对象方法注解和类注解中的数据源标识
         * @throws Exception
         */
        public void resetDataSource(Class<?> clazz,Method method)throws Exception{
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 默认使用类注解
            if (clazz.isAnnotationPresent(DataSource.class)) {
                DataSource dataSource = clazz.getAnnotation(DataSource.class);//如果存在@DataSource注解,则返回这个注解,否则返回 null
                DynamicDataSourceHolder.setDataSourceHolder(dataSource.value());//把数据源名字绑定到本地线程
            }
            // 方法注解可以覆盖类注解
            Method method2 = clazz.getMethod(method.getName(), parameterTypes);
            if (method2!=null&&method2.isAnnotationPresent(DataSource.class)) {
                DataSource dataSource = method2.getAnnotation(DataSource.class);
                DynamicDataSourceHolder.setDataSourceHolder(dataSource.value());
            }
        }
    }

    6、设置数据库事务切面和切换数据库切面执行的顺序

    <bean id="DataSourceAspect" class="com.sgl.dataSource" />
    <aop:config>  
        <aop:pointcut id="transactionPointCut" expression="execution(* com.sgl.service.*.*(..))" />  
    <!--     <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="transactionPointCut" order="1"/>   -->
        <aop:advisor advice-ref="DataSourceAspect" pointcut-ref="transactionPointCut" order="1"/>  
        <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointCut" order="2" />  
    </aop:config>
  • 相关阅读:
    应对高并发场景的redis加锁技巧
    Spring中@Transactional事务回滚(含实例具体解说,附源代码)
    计算机网络10--计算机网络体系结构简单介绍
    IIS身份验证的配置
    AMR音频文件格式分析
    IOS版本号被拒的经历
    两分钟读懂《成大事者不纠结》——读书笔记
    同一个TextView设置不同的颜色和大小
    似非而是的程序猿悖论---为什么救火比防火更加吃香?
    OS
  • 原文地址:https://www.cnblogs.com/a591378955/p/8554838.html
Copyright © 2020-2023  润新知