zoukankan      html  css  js  c++  java
  • AbstractRoutingDataSource 实现动态切换数据源

    扩展AbstractRoutingDataSource类

    package com.datasource.test.util.database;
     
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     
    /**
     * 获取数据源(依赖于spring)
     */
    public class DynamicDataSource extends AbstractRoutingDataSource{
        @Override
        protected Object determineCurrentLookupKey() {
            return DataSourceHolder.getDataSource();
        }
    }

    DataSourceHolder这个类则是我们自己封装的对数据源进行操作的类:

    package com.datasource.test.util.database;
     
    /**
     * 数据源操作
     */
    public class DataSourceHolder {
        //线程本地环境
        private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
        //设置数据源
        public static void setDataSource(String customerType) {
            dataSources.set(customerType);
        }
        //获取数据源
        public static String getDataSource() {
            return (String) dataSources.get();
        }
        //清除数据源
        public static void clearDataSource() {
            dataSources.remove();
        }
     
    }

    在注解中使用

    @DataSource(name=DataSource.slave1)
    public List getProducts(){}

    注解定义:

    package com.datasource.test.util.database;
     
    import java.lang.annotation.*;
     
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DataSource {
        String name() default DataSource.master;
     
        public static String master = "dataSource1";
     
        public static String slave1 = "dataSource2";
     
        public static String slave2 = "dataSource3";
     
    }

    定义拦截器,拦截DataSource的值

    public class DataSourceAspect {
    
        /**
         * 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
         * 
         * @param point
         * @throws Exception
         */
        public void intercept(JoinPoint point) throws Exception {
            Class<?> target = point.getTarget().getClass();
            MethodSignature signature = (MethodSignature) point.getSignature();
            // 默认使用目标类型的注解,如果没有则使用其实现接口的注解
            for (Class<?> clazz : target.getInterfaces()) {
                resolveDataSource(clazz, signature.getMethod());
            }
            resolveDataSource(target, signature.getMethod());
        }
    
        /**
         * 提取目标对象方法注解和类型注解中的数据源标识
         * 
         * @param clazz
         * @param method
         */
        private void resolveDataSource(Class<?> clazz, Method method) {
            try {
                Class<?>[] types = method.getParameterTypes();
                // 默认使用类型注解
                if (clazz.isAnnotationPresent(DataSource.class)) {
                    DataSource source = clazz.getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.setDataSource(source.value());
                }
                // 方法注解可以覆盖类型注解
                Method m = clazz.getMethod(method.getName(), types);
                if (m != null && m.isAnnotationPresent(DataSource.class)) {
                    DataSource source = m.getAnnotation(DataSource.class);
                    DynamicDataSourceHolder.setDataSource(source.value());
                }
            } catch (Exception e) {
                System.out.println(clazz + ":" + e.getMessage());
            }
        }
    
    }

    spring 中配置拦截器:

    <bean id="dataSourceAspect" class="com.test.context.datasource.DataSourceAspect" />
        <aop:config>
            <aop:aspect ref="dataSourceAspect">
                <!-- 拦截所有service方法 -->
                <aop:pointcut id="dataSourcePointcut" expression="execution(* com.test.*.dao.*.*(..))"/>
                <aop:before pointcut-ref="dataSourcePointcut" method="intercept" />
            </aop:aspect>
        </aop:config>
    </bean>

    多数据源配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Spring 数据库相关配置 放在这里 -->
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
     
        <bean id = "dataSource1" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">   
            <property name="url" value="${db1.url}"/>
            <property name = "user" value = "${db1.user}"/>
            <property name = "password" value = "${db1.pwd}"/>
            <property name="autoReconnect" value="true"/>
            <property name="useUnicode"  value="true"/>
            <property name="characterEncoding" value="UTF-8"/>
        </bean>
     
        <bean id = "dataSource2" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
            <property name="url" value="${db2.url}"/>
            <property name = "user" value = "${db2.user}"/>
            <property name = "password" value = "${db2.pwd}"/>
            <property name="autoReconnect" value="true"/>
            <property name="useUnicode"  value="true"/>
            <property name="characterEncoding" value="UTF-8"/>
        </bean>
     
        <bean id = "dataSource3" class = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
            <property name="url" value="${db3.url}"/>
            <property name = "user" value = "${db3.user}"/>
            <property name = "password" value = "${db3.pwd}"/>
            <property name="autoReconnect" value="true"/>
            <property name="useUnicode"  value="true"/>
            <property name="characterEncoding" value="UTF-8"/>
        </bean>
        <!-- 配置多数据源映射关系 -->
        <bean id="dataSource" class="com.datasource.test.util.database.DynamicDataSource">
            <property name="targetDataSources">
                <map key-type="java.lang.String">
            <entry key="dataSource1" value-ref="dataSource1"></entry>
                    <entry key="dataSource2" value-ref="dataSource2"></entry>
                    <entry key="dataSource3" value-ref="dataSource3"></entry>
                </map>
            </property>
        <!-- 默认目标数据源为你主库数据源 -->
            <property name="defaultTargetDataSource" ref="dataSource1"/>
        </bean>
     
        <bean id="sessionFactoryHibernate" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">com.datasource.test.util.database.ExtendedMySQLDialect</prop>
                    <prop key="hibernate.show_sql">${SHOWSQL}</prop>
                    <prop key="hibernate.format_sql">${SHOWSQL}</prop>
                    <prop key="query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
                    <prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
                    <prop key="hibernate.c3p0.max_size">30</prop>
                    <prop key="hibernate.c3p0.min_size">5</prop>
                    <prop key="hibernate.c3p0.timeout">120</prop>
                    <prop key="hibernate.c3p0.idle_test_period">120</prop>
                    <prop key="hibernate.c3p0.acquire_increment">2</prop>
                    <prop key="hibernate.c3p0.validate">true</prop>
                    <prop key="hibernate.c3p0.max_statements">100</prop>
                </props>
            </property>
        </bean>
     
        <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
            <property name="sessionFactory" ref="sessionFactoryHibernate"/>
        </bean>
     
        <bean id="dataSourceExchange" class="com.datasource.test.util.database.DataSourceExchange"/>
     
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactoryHibernate"/>
        </bean>
     
        <tx:advice id="txAdvice" transaction-manager="transactionManager">
            <tx:attributes>
                <tx:method name="insert*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="add*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="update*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="modify*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="edit*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="del*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="save*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="send*" propagation="NESTED" rollback-for="Exception"/>
                <tx:method name="get*" read-only="true"/>
                <tx:method name="find*" read-only="true"/>
                <tx:method name="query*" read-only="true"/>
                <tx:method name="search*" read-only="true"/>
                <tx:method name="select*" read-only="true"/>
                <tx:method name="count*" read-only="true"/>
            </tx:attributes>
        </tx:advice>
     
        <aop:config>
            <aop:pointcut id="service" expression="execution(* com.datasource..*.service.*.*(..))"/>
            <!-- 关键配置,切换数据源一定要比持久层代码更先执行(事务也算持久层代码) -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="service" order="2"/>
            <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="service" order="1"/>
        </aop:config>
     
    </beans>

    PS: 以上是从两个地址复制的代码,不作准确性验证,但思路就是这样。

  • 相关阅读:
    session判断重复提交
    logback日志配置
    quartz动态job工具类 serviceh注入问题
    mysql10061登录失败错误解决方案
    安装步骤
    无法启动此程序,因为计算机丢失MSVCP120.dll
    Bigdecimal: Non-terminating decimal expansion; no exact representable decimal result.
    replace()函数用法
    随机读取表中一条数据
    oracle table()函数
  • 原文地址:https://www.cnblogs.com/Gyoung/p/7147677.html
Copyright © 2011-2022 走看看