zoukankan      html  css  js  c++  java
  • 使用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>
  • 相关阅读:
    ActiveMQ 即时通讯服务 浅析
    Asp.net Mvc (Filter及其执行顺序)
    ActiveMQ基本介绍
    ActiveMQ持久化消息的三种方式
    Windows Azure Virtual Machine (27) 使用psping工具,测试Azure VM网络连通性
    Azure China (10) 使用Azure China SAS Token
    Windows Azure Affinity Groups (3) 修改虚拟网络地缘组(Affinity Group)的配置
    Windows Azure Storage (22) Azure Storage如何支持多级目录
    Windows Azure Virtual Machine (26) 使用高级存储(SSD)和DS系列VM
    Azure Redis Cache (2) 创建和使用Azure Redis Cache
  • 原文地址:https://www.cnblogs.com/a591378955/p/8554838.html
Copyright © 2011-2022 走看看