扩展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: 以上是从两个地址复制的代码,不作准确性验证,但思路就是这样。