擴展AbstractRoutingDataSource類java
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這個類則是咱們本身封裝的對數據源進行操做的類:mysql
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(); } }
在註解中使用spring
@DataSource(name=DataSource.slave1) public List getProducts(){}
註解定義:sql
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 中配置攔截器:express
<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>
多數據源配置:session
<?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: 以上是從兩個地址複製的代碼,不做準確性驗證,但思路就是這樣。ide