本文的幾個(gè)關(guān)鍵詞,分布式數(shù)據(jù)源,數(shù)據(jù)源的動(dòng)態(tài)尋找,分布式事務(wù)JTA實(shí)現(xiàn)。
對(duì)于一些較大規(guī)模的應(yīng)用,單個(gè)數(shù)據(jù)源是無法支撐起龐大的用戶量,需要引入多數(shù)據(jù)源,水平層面進(jìn)行分庫分表,降低單個(gè)DB的負(fù)載。接下來,我們程序里里面需 要管理不同數(shù)據(jù)源之前的程序調(diào)用,保證功能是WORK的。另外,跨庫就意味著之前單DB的事務(wù)就失效了,所以J2EE提出了JTA,分布式的事務(wù)管理,往 簡單了說,就是2步提交(two phase),比單步提交更苛刻。實(shí)際上他有兩個(gè)容器來管理,一個(gè)是資源管理器,一個(gè)是事務(wù)管理。小伙伴們可以發(fā)現(xiàn),這是一個(gè)環(huán)環(huán)相扣的過程。想解決一個(gè) 問題,你就得解決這幾個(gè)相關(guān)的問題。以下代碼,我也是參考了前輩們的思想,進(jìn)行了改造。
第一步:XA數(shù)據(jù)源定義
選定義一個(gè)抽象的父類源,這樣子類可以直接繼承
基于SPRING的AbstractRoutingDataSource動(dòng)態(tài)數(shù)據(jù)路由定義
我這里是使用MYBATIS來進(jìn)行ORM映射,配置如下
最后就是JTA的實(shí)現(xiàn)配置
具體的代碼,請(qǐng)點(diǎn)擊GITHUB查看https://github.com/igool/spring-jta-mybatis
對(duì)于一些較大規(guī)模的應(yīng)用,單個(gè)數(shù)據(jù)源是無法支撐起龐大的用戶量,需要引入多數(shù)據(jù)源,水平層面進(jìn)行分庫分表,降低單個(gè)DB的負(fù)載。接下來,我們程序里里面需 要管理不同數(shù)據(jù)源之前的程序調(diào)用,保證功能是WORK的。另外,跨庫就意味著之前單DB的事務(wù)就失效了,所以J2EE提出了JTA,分布式的事務(wù)管理,往 簡單了說,就是2步提交(two phase),比單步提交更苛刻。實(shí)際上他有兩個(gè)容器來管理,一個(gè)是資源管理器,一個(gè)是事務(wù)管理。小伙伴們可以發(fā)現(xiàn),這是一個(gè)環(huán)環(huán)相扣的過程。想解決一個(gè) 問題,你就得解決這幾個(gè)相關(guān)的問題。以下代碼,我也是參考了前輩們的思想,進(jìn)行了改造。
第一步:XA數(shù)據(jù)源定義
選定義一個(gè)抽象的父類源,這樣子類可以直接繼承
1 <!-- 兩個(gè)數(shù)據(jù)源的功用配置,方便下面直接引用 -->
2 <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
3 destroy-method="close" abstract="true">
4 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
5 <property name="poolSize" value="10" />
6 <property name="minPoolSize" value="10"/>
7 <property name="maxPoolSize" value="30"/>
8 <property name="borrowConnectionTimeout" value="60"/>
9 <property name="reapTimeout" value="20"/>
10 <!-- 最大空閑時(shí)間 -->
11 <property name="maxIdleTime" value="60"/>
12 <property name="maintenanceInterval" value="60" />
13 <property name="loginTimeout" value="60"/>
14 <property name="logWriter" value="60"/>
15 <property name="testQuery">
16 <value>select 1</value>
17 </property>
18
19 </bean>
20
A源2 <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init"
3 destroy-method="close" abstract="true">
4 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/>
5 <property name="poolSize" value="10" />
6 <property name="minPoolSize" value="10"/>
7 <property name="maxPoolSize" value="30"/>
8 <property name="borrowConnectionTimeout" value="60"/>
9 <property name="reapTimeout" value="20"/>
10 <!-- 最大空閑時(shí)間 -->
11 <property name="maxIdleTime" value="60"/>
12 <property name="maintenanceInterval" value="60" />
13 <property name="loginTimeout" value="60"/>
14 <property name="logWriter" value="60"/>
15 <property name="testQuery">
16 <value>select 1</value>
17 </property>
18
19 </bean>
20
1 <!-- 配置第一個(gè)數(shù)據(jù)源 -->
2 <bean id="dataSource_a" parent="abstractXADataSource">
3 <!-- value只要兩個(gè)數(shù)據(jù)源不同就行,隨便取名 -->
4 <property name="uniqueResourceName" value="mysql/sitestone" />
5 <property name="xaDataSourceClassName"
6 value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
7 <property name="xaProperties">
8 <props>
9 <prop key="URL">${jdbc.url.spider}</prop>
10 <prop key="user">${jdbc.username}</prop>
11 <prop key="password">${jdbc.password}</prop>
12 </props>
13 </property>
14 </bean>
2 <bean id="dataSource_a" parent="abstractXADataSource">
3 <!-- value只要兩個(gè)數(shù)據(jù)源不同就行,隨便取名 -->
4 <property name="uniqueResourceName" value="mysql/sitestone" />
5 <property name="xaDataSourceClassName"
6 value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
7 <property name="xaProperties">
8 <props>
9 <prop key="URL">${jdbc.url.spider}</prop>
10 <prop key="user">${jdbc.username}</prop>
11 <prop key="password">${jdbc.password}</prop>
12 </props>
13 </property>
14 </bean>
B 源
1 <!-- 配置第二個(gè)數(shù)據(jù)源-->
2 <bean id="dataSource_b" parent="abstractXADataSource">
3 <!-- value只要兩個(gè)數(shù)據(jù)源不同就行,隨便取名 -->
4 <property name="uniqueResourceName" value="mysql/sitesttwo" />
5 <property name="xaDataSourceClassName"
6 value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
7 <property name="xaProperties">
8 <props>
9 <prop key="URL">${jdbc_tb.url.spider}</prop>
10 <prop key="user">${jdbc_tb.username}</prop>
11 <prop key="password">${jdbc_tb.password}</prop>
12 </props>
13 </property>
14 </bean>
2 <bean id="dataSource_b" parent="abstractXADataSource">
3 <!-- value只要兩個(gè)數(shù)據(jù)源不同就行,隨便取名 -->
4 <property name="uniqueResourceName" value="mysql/sitesttwo" />
5 <property name="xaDataSourceClassName"
6 value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
7 <property name="xaProperties">
8 <props>
9 <prop key="URL">${jdbc_tb.url.spider}</prop>
10 <prop key="user">${jdbc_tb.username}</prop>
11 <prop key="password">${jdbc_tb.password}</prop>
12 </props>
13 </property>
14 </bean>
基于SPRING的AbstractRoutingDataSource動(dòng)態(tài)數(shù)據(jù)路由定義
1 <bean name="dynamicDatasource" class="com.***.spring.datasource.CustomerDatasource">
2 <property name="targetDataSources">
3 <map>
4 <entry key="ds_1" value-ref="dataSource_a"/>
5 <entry key="ds_2" value-ref="dataSource_b"/>
6 </map>
7 </property>
8 <property name="defaultTargetDataSource" ref="dataSource_a" />
9 </bean>
2 <property name="targetDataSources">
3 <map>
4 <entry key="ds_1" value-ref="dataSource_a"/>
5 <entry key="ds_2" value-ref="dataSource_b"/>
6 </map>
7 </property>
8 <property name="defaultTargetDataSource" ref="dataSource_a" />
9 </bean>
1 <bean id="sqlSessionFactorya" class="org.mybatis.spring.SqlSessionFactoryBean">
2 <property name="dataSource" ref="dataSource_a"/>
3 <property name="typeAliasesPackage" value="com.****.spring.dschange.bean" />
4 <!-- mapper和resultmap配置路徑 -->
5 <property name="mapperLocations">
6 <list>
7 <!-- 表示在com.**目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結(jié)尾所有文件 -->
8 <value>classpath:com/***/spring/dschange/mapper/ShopMapper.xml</value>
9 </list>
10 </property>
11 </bean>
12 <bean id="sqlSessionFactoryb" class="org.mybatis.spring.SqlSessionFactoryBean">
13 <property name="dataSource" ref="dataSource_b"/>
14 <property name="typeAliasesPackage" value="com.****.spring.dschange.bean" />
15 <!-- mapper和resultmap配置路徑 -->
16 <property name="mapperLocations">
17 <list>
18 <!-- 表示在com.***目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結(jié)尾所有文件 -->
19 <value>classpath:com/***/spring/dschange/mapper/ShopMapper.xml</value>
20 </list>
21 </property>
22 </bean>
接下來,一個(gè)比較關(guān)鍵的地方是對(duì)MYBATIS的CustomSqlSessionTemplate的重寫,主要是引入動(dòng)態(tài)數(shù)據(jù)源sqlSessionFactory。針對(duì)不同的數(shù)據(jù)庫,調(diào)用其對(duì)應(yīng)的會(huì)話工廠,這對(duì)JTA是否啟用,比較重要。2 <property name="dataSource" ref="dataSource_a"/>
3 <property name="typeAliasesPackage" value="com.****.spring.dschange.bean" />
4 <!-- mapper和resultmap配置路徑 -->
5 <property name="mapperLocations">
6 <list>
7 <!-- 表示在com.**目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結(jié)尾所有文件 -->
8 <value>classpath:com/***/spring/dschange/mapper/ShopMapper.xml</value>
9 </list>
10 </property>
11 </bean>
12 <bean id="sqlSessionFactoryb" class="org.mybatis.spring.SqlSessionFactoryBean">
13 <property name="dataSource" ref="dataSource_b"/>
14 <property name="typeAliasesPackage" value="com.****.spring.dschange.bean" />
15 <!-- mapper和resultmap配置路徑 -->
16 <property name="mapperLocations">
17 <list>
18 <!-- 表示在com.***目錄下的任意包下的resultmap包目錄中,以-resultmap.xml或-mapper.xml結(jié)尾所有文件 -->
19 <value>classpath:com/***/spring/dschange/mapper/ShopMapper.xml</value>
20 </list>
21 </property>
22 </bean>
1 @Override
2 public SqlSessionFactory getSqlSessionFactory() {
3
4 SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(DataSourceKeyHolder.getDataSourceKey());
5 if (targetSqlSessionFactory != null) {
6 return targetSqlSessionFactory;
7 } else if (defaultTargetSqlSessionFactory != null) {
8 return defaultTargetSqlSessionFactory;
9 } else {
10 Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
11 Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
12 }
13 return this.sqlSessionFactory;
14 }
XML配置
掃描配置2 public SqlSessionFactory getSqlSessionFactory() {
3
4 SqlSessionFactory targetSqlSessionFactory = targetSqlSessionFactorys.get(DataSourceKeyHolder.getDataSourceKey());
5 if (targetSqlSessionFactory != null) {
6 return targetSqlSessionFactory;
7 } else if (defaultTargetSqlSessionFactory != null) {
8 return defaultTargetSqlSessionFactory;
9 } else {
10 Assert.notNull(targetSqlSessionFactorys, "Property 'targetSqlSessionFactorys' or 'defaultTargetSqlSessionFactory' are required");
11 Assert.notNull(defaultTargetSqlSessionFactory, "Property 'defaultTargetSqlSessionFactory' or 'targetSqlSessionFactorys' are required");
12 }
13 return this.sqlSessionFactory;
14 }
XML配置
1 <!-- 配置自定義的SqlSessionTemplate模板,注入相關(guān)配置 -->
2 <bean id="sqlSessionTemplate" class="com.amos.spring.mybatis.CustomSqlSessionTemplate" scope="prototype">
3 <constructor-arg ref="sqlSessionFactorya" />
4 <property name="targetSqlSessionFactorys">
5 <map>
6 <entry value-ref="sqlSessionFactorya" key="ds_1"/>
7 <entry value-ref="sqlSessionFactoryb" key="ds_2"/>
8 </map>
9 </property>
10 </bean>
2 <bean id="sqlSessionTemplate" class="com.amos.spring.mybatis.CustomSqlSessionTemplate" scope="prototype">
3 <constructor-arg ref="sqlSessionFactorya" />
4 <property name="targetSqlSessionFactorys">
5 <map>
6 <entry value-ref="sqlSessionFactorya" key="ds_1"/>
7 <entry value-ref="sqlSessionFactoryb" key="ds_2"/>
8 </map>
9 </property>
10 </bean>
1 <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
3 <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
4 <property name="markerInterface" value="com.*****.spring.dschange.mapper.SqlMapper"/>
5 </bean>
1 <!-- jta -->
2 <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
3 init-method="init" destroy-method="close">
4 <property name="forceShutdown">
5 <value>true</value>
6 </property>
7 </bean>
8
9 <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
10 <property name="transactionTimeout" value="300" />
11 </bean>
12
13 <bean id="springTransactionManager"
14 class="org.springframework.transaction.jta.JtaTransactionManager">
15 <property name="transactionManager">
16 <ref bean="atomikosTransactionManager" />
17 </property>
18 <property name="userTransaction">
19 <ref bean="atomikosUserTransaction" />
20 </property>
21 </bean>
22 <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
2 <property name="basePackage" value="com.****.spring.dschange.mapper" />2 <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
3 init-method="init" destroy-method="close">
4 <property name="forceShutdown">
5 <value>true</value>
6 </property>
7 </bean>
8
9 <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
10 <property name="transactionTimeout" value="300" />
11 </bean>
12
13 <bean id="springTransactionManager"
14 class="org.springframework.transaction.jta.JtaTransactionManager">
15 <property name="transactionManager">
16 <ref bean="atomikosTransactionManager" />
17 </property>
18 <property name="userTransaction">
19 <ref bean="atomikosUserTransaction" />
20 </property>
21 </bean>
22 <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
3 <property name="sqlSessionTemplateBeanName" value="sqlSessionTemplate"/>
4 <property name="markerInterface" value="com.*****.spring.dschange.mapper.SqlMapper"/>
5 </bean>
最后就是JTA的實(shí)現(xiàn)配置
1 <!-- jta -->
2 <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
3 init-method="init" destroy-method="close">
4 <property name="forceShutdown">
5 <value>true</value>
6 </property>
7 </bean>
8
9 <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
10 <property name="transactionTimeout" value="300" />
11 </bean>
12
13 <bean id="springTransactionManager"
14 class="org.springframework.transaction.jta.JtaTransactionManager">
15 <property name="transactionManager">
16 <ref bean="atomikosTransactionManager" />
17 </property>
18 <property name="userTransaction">
19 <ref bean="atomikosUserTransaction" />
20 </property>
21 </bean>
22 <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
以上,XML配置相關(guān)的東西已經(jīng)完成。2 <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
3 init-method="init" destroy-method="close">
4 <property name="forceShutdown">
5 <value>true</value>
6 </property>
7 </bean>
8
9 <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
10 <property name="transactionTimeout" value="300" />
11 </bean>
12
13 <bean id="springTransactionManager"
14 class="org.springframework.transaction.jta.JtaTransactionManager">
15 <property name="transactionManager">
16 <ref bean="atomikosTransactionManager" />
17 </property>
18 <property name="userTransaction">
19 <ref bean="atomikosUserTransaction" />
20 </property>
21 </bean>
22 <tx:annotation-driven transaction-manager="springTransactionManager" proxy-target-class="true" />
具體的代碼,請(qǐng)點(diǎn)擊GITHUB查看https://github.com/igool/spring-jta-mybatis
我的微信公眾號(hào),歡迎溝通學(xué)習(xí)。
