正確可行的解決方法:使用Spring提供的AbstractRoutingDataSource類來根據請求路由到不同的數據源。具體做法是
先設置兩個不同的dataSource代表不同的數據源,再建一個總的dynamicDataSource,根據不同的請求去設置dynamicDataSource。代碼如下:
配置文件spring-mybatis.xml
<!--統一的dataSource--> <bean id="dynamicDataSource" class="path.to.DynamicDataSource" > <property name="targetDataSources"> <map key-type="java.lang.String"> <!--通過不同的key決定用哪個dataSource--> <entry value-ref="dataSource" key="dataSource"></entry> <entry value-ref="mssqlDataSource" key="mssqlDataSource"></entry> </map> </property> <!--設置默認的dataSource--> <property name="defaultTargetDataSource" ref="dataSource"> </property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> <!-- 初始化連接大小 --> <property name="initialSize" value="${jdbc.initialSize}"></property> <!-- 連接池最大數量 --> <property name="maxActive" value="${jdbc.maxActive}"></property> <!-- 連接池最大空閑 --> <property name="maxIdle" value="${jdbc.maxIdle}"></property> <!-- 連接池最小空閑 --> <property name="minIdle" value="${jdbc.minIdle}"></property> <!-- 獲取連接最大等待時間 --> <property name="maxWait" value="${jdbc.maxWait}"></property> </bean> <!--電影票數據庫是mssql2008,單獨的數據庫,配置如下--> <bean id="mssqlDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc-mssql.driver}" /> <property name="url" value="${jdbc-mssql.url}" /> <property name="username" value="${jdbc-mssql.username}" /> <property name="password" value="${jdbc-mssql.password}" /> <!-- 初始化連接大小 --> <property name="initialSize" value="${jdbc.initialSize}"></property> <!-- 連接池最大數量 --> <property name="maxActive" value="${jdbc.maxActive}"></property> <!-- 連接池最大空閑 --> <property name="maxIdle" value="${jdbc.maxIdle}"></property> <!-- 連接池最小空閑 --> <property name="minIdle" value="${jdbc.minIdle}"></property> <!-- 獲取連接最大等待時間 --> <property name="maxWait" value="${jdbc.maxWait}"></property> </bean> <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dynamicDataSource" /> <!-- 自動掃描mapping.xml文件 --> <property name="mapperLocations" value="classpath:path/to/mapping/*.xml"></property> </bean> <!-- DAO接口所在包名,Spring會自動查找其下的類 --> <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="path.to.dao" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> </bean> <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dynamicDataSource" /> </bean>
DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return CustomerContextHolder.getCustomerType(); } }
CustomerContextHolder.java
public class CustomerContextHolder { public static final String DATA_SOURCE_MYSQL = "dataSource"; public static final String DATA_SOURCE_MSSQL = "mssqlDataSource"; //用ThreadLocal來設置當前線程使用哪個dataSource private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setCustomerType(String customerType) { contextHolder.set(customerType); } public static String getCustomerType() { String dataSource = contextHolder.get(); if (StringUtils.isEmpty(dataSource)) { return DATA_SOURCE_MYSQL; }else { return dataSource; } } public static void clearCustomerType() { contextHolder.remove(); } }
ServiceImpl.java
CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_MSSQL);
值得注意的是在CustomerContextHolder.java中使用了ThreadLocal類的set方法來設置當前線程要選擇的dataSource,看一下set方法的源碼:
ThreadLocal.set()
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
顯而易見,獲取當前線程,並且使用一個hashmap把需要存儲的值設置進去。因為tomcat是用的線程池來處理每個請求,所以用ThreadLocal可以保證線程安全問題。同時這個AbstractRoutingDataSource類也值得好好研究一下。
總結
其實這個方案不僅僅可以用來處理不同數據源的問題,同時業務量上來之后需要把數據庫進行主從分離或是把一個庫分為多個庫,都需要用到這樣的做法。這次暴露的問題確實也了解了不少,繼續學習吧!
原地址:http://www.cnblogs.com/puyangsky/p/6133553.html
親測可用!