? 使用平臺(tái): 1.??? Mysql 5.0.8 2.??? ibatis 2.3.4 3.??? spring 2.5 4.??? eclipse 3.3 完成賬戶的轉(zhuǎn)賬功能,在出現(xiàn)異常時(shí)回滾。由spring管理各個(gè)類之間的關(guān)系和事務(wù)處理。 - 首先建立兩個(gè)數(shù)據(jù)庫(kù),一個(gè)在本機(jī)上,一個(gè)在遠(yuǎn)端機(jī)上,腳本分別如下
?1 DROP?DATABASE?IF?EXISTS?testdb_a_zhanglong; ?2 ?3 CREATE?DATABASE?testdb_a_zhanglong; ?4 ?5 USE?testdb_a_zhanglong; ?6 ?7 CREATE?TABLE?Account( ?8 ????aid????????INT????????AUTO_INCREMENT, ?9 ????owner????????VARCHAR(20)????NOT?NULL, 10 ????balance????????Float(10,3)????DEFAULT?0.0, 11 ????PRIMARY?KEY(aid) 12 ); 13 14 INSERT?INTO?Account?(owner,balance)?VALUES?('zhanglong',10000.00); 15 ?1 DROP?DATABASE?IF?EXISTS?testdb_b_zhanglong; ?2 ?3 CREATE?DATABASE?testdb_b_zhanglong; ?4 ?5 USE?testdb_b_zhanglong; ?6 ?7 CREATE?TABLE?Account( ?8 ????aid????????INT????????AUTO_INCREMENT, ?9 ????owner????????VARCHAR(20)????NOT?NULL, 10 ????balance????????Float(10,3)????DEFAULT?0.0, 11 ????PRIMARY?KEY(aid) 12 ); 13 14 INSERT?INTO?Account?(owner,balance)?VALUES?('zhengtao',10000.00); 15 在這里兩個(gè)數(shù)據(jù)庫(kù)是完全一樣的,是為了方便起見(jiàn)。 - 建立Account的JavaBean類
1 package?com.aaron.atomikos.domain; 2 3 public?class?Account? { 4 5 ????private?int?accountId; 6 ????private?String?owner; 7 ????private?float?balance; 8 } 9 - 建立業(yè)務(wù)層接口AccountService
?1 package?com.aaron.atomikos.service; ?2 import?com.aaron.atomikos.domain.Account; ?3 public?interface?AccountService? { ?4 ????void?addAccount(Account?account); ?5 ???? ?6 ????/**?*//** ?7 ?????*?轉(zhuǎn)賬 ?8 ?????*?@param?ida ?9 ?????*?@param?idb 10 ?????*?@param?amount 11 ?????*?@return 12 ?????*/ 13 ????boolean?transfer(int?ida,int?idb,float?amount);? 14 ???? 15 ????Account?getAccountById(int?id); 16 } 17 ?
- 建立業(yè)務(wù)層接口實(shí)現(xiàn)類AccountServiceImpl
?1 package?com.aaron.atomikos.service.impl; ?2 import?com.aaron.atomikos.dao.AccountDao; ?3 import?com.aaron.atomikos.domain.Account; ?4 import?com.aaron.atomikos.service.AccountService; ?5 public?class?AccountServiceImpl?implements?AccountService? { ?6 ?7 ????//?數(shù)據(jù)庫(kù)a的Dao類 ?8 ????private?AccountDao?accountaDao; ?9 ????//?數(shù)據(jù)庫(kù)b的Dao類 10 ????private?AccountDao?accountbDao; 11 ????public?AccountDao?getAccountbDao()? { 12 ????????return?accountbDao; 13 ????} 14 ????public?void?setAccountbDao(AccountDao?accountbDao)? { 15 ????????this.accountbDao?=?accountbDao; 16 ????} 17 ????public?AccountDao?getAccountDao()? { 18 ????????return?accountaDao; 19 ????} 20 ????public?void?setAccountaDao(AccountDao?accountaDao)? { 21 ????????this.accountaDao?=?accountaDao; 22 ????} 23 ????@Override 24 ????public?void?addAccount(Account?account)? { 25 ????????accountaDao.insertAccount(account); 26 ????} 27 28 ????@Override 29 ????public?boolean?transfer(int?ida,?int?idb,?float?amount)? { 30 ????????boolean?isSuccess?=?false; 31 32 ????????Account?accounta?=?new?Account(); 33 ????????Account?accountb?=?new?Account(); 34 35 ????????//檢查賬戶是否存在 36 ????????if?(!accountaDao.isExists(ida)?||?!accountbDao.isExists(idb))? { 37 ????????????throw?new?RuntimeException("賬戶"?+?ida?+?"或"?+?idb?+?"不存在"); 38 ????????} 39 ???????? 40 ????????//得到賬戶余額 41 ????????float?balancea?=?accountaDao.getBalance(ida); 42 ????????float?balanceb?=?accountbDao.getBalance(idb); 43 ????????if?(balancea?<?amount)? { 44 ????????????throw?new?RuntimeException("賬戶"?+?ida?+?"的余額不足!"); 45 ????????} 46 ???????? 47 ????????//轉(zhuǎn)賬 48 ????????accounta.setAccountId(ida); 49 ????????accountb.setAccountId(idb); 50 ????????accounta.setBalance(balancea?-?amount); 51 ????????accountb.setBalance(balanceb?+?amount); 52 53 ????????accountaDao.updateAccount(accounta); 54 55 ????????//兩次更新之間如果跑出異常,則會(huì)回滾操作 56 ????????//?if(true){ 57 ????????//?throw?new?RuntimeException(); 58 ????????//?} 59 60 ????????accountbDao.updateAccount(accountb); 61 ????????isSuccess?=?true; 62 ????????return?isSuccess; 63 ????} 64 65 ????@Override 66 ????public?Account?getAccountById(int?id)? { 67 ????????return?accountaDao.selectAccountById(id); 68 ????} 69 } 70 建立DAO層AccountDao,繼承自spring的SqlMapClientDaoSupport,這里為了簡(jiǎn)單起見(jiàn),沒(méi)有使用接口,直接寫(xiě)了具體實(shí)現(xiàn)類
?1 package?com.aaron.atomikos.dao; ?2 ?3 import?org.springframework.orm.ibatis.support.SqlMapClientDaoSupport; ?4 ?5 import?com.aaron.atomikos.domain.Account; ?6 ?7 public?class?AccountDao?extends?SqlMapClientDaoSupport? { ?8 ?9 ????/**?*//** 10 ?????*?插入賬戶 11 ?????*? 12 ?????*?@param?account 13 ?????*/ 14 ????public?void?insertAccount(Account?account)? { 15 ????????getSqlMapClientTemplate().insert("Account.insertAccount",?account); 16 ????} 17 18 ????/**?*//** 19 ?????*?根據(jù)id刪除賬戶 20 ?????*? 21 ?????*?@param?id 22 ?????*/ 23 ????public?void?deleteAccountById(int?id)? { 24 ????????getSqlMapClientTemplate().delete("Account.deleteAccountById",?id); 25 ????} 26 27 ????/**?*//** 28 ?????*?更新賬戶余額 29 ?????*? 30 ?????*?@param?account 31 ?????*/ 32 ????public?void?updateAccount(Account?account)? { 33 ????????getSqlMapClientTemplate().update("Account.updateBalance",?account); 34 ????} 35 36 ????/**?*//** 37 ?????*?根據(jù)id查找賬戶 38 ?????*? 39 ?????*?@param?id 40 ?????*?@return 41 ?????*/ 42 ????public?Account?selectAccountById(int?id)? { 43 ????????return?(Account)?getSqlMapClientTemplate().queryForObject( 44 ????????????????"Account.selectAccountById",?id); 45 ????} 46 47 ????/**?*//** 48 ?????*?根據(jù)賬戶編號(hào)得到余額 49 ?????*? 50 ?????*?@param?idb 51 ?????*?@return 52 ?????*/ 53 ????public?float?getBalance(int?idb)? { 54 ????????return?Float.parseFloat(getSqlMapClientTemplate().queryForObject( 55 ????????????????"Account.selectBalance",?idb).toString()); 56 ????} 57 58 ????/**?*//** 59 ?????*?判斷賬戶是否存在 60 ?????*? 61 ?????*?@param?ida 62 ?????*?@return 63 ?????*/ 64 ????public?boolean?isExists(int?ida)? { 65 ????????return?(Integer)?getSqlMapClientTemplate().queryForObject( 66 ????????????????"Account.selectCount",?ida)?>?0???true?:?false; 67 ????} 68 } 編寫(xiě)iBATIS的sql映射文件Account.xml
?1 <?xml?version="1.0"?encoding="UTF-8"?> ?2 <!DOCTYPE?sqlMap?PUBLIC?"-//ibatis.apache.org//DTD?SQL?Map?2.0//EN"?"http://ibatis.apache.org/dtd/sql-map-2.dtd"?> ?3 <sqlMap?namespace="Account"> ?4 ????<typeAlias?alias="Account"?type="com.aaron.atomikos.domain.Account"?/> ?5 ????<select?id="selectAccountById"?parameterClass="int"?resultClass="Account"> ?6 ????????select?*?from?Account?where?aid?=?#value# ?7 ????</select> ?8 ???? ?9 ????<select?id="selectBalance"?parameterClass="int"?resultClass="float"> 10 ????????select?balance?from?Account?where?aid?=?#value# 11 ????</select> 12 ???? 13 ????<select?id="selectCount"?parameterClass="int"?resultClass="int"> 14 ????????select?count(*)?from?Account?where?aid?=?#value# 15 ????</select> 16 ???? 17 ????<update?id="updateBalance"?parameterClass="Account"> 18 ????????update?Account?set?balance?=?#balance#?where?aid?=?#accountId# 19 ????</update> 20 ???? 21 ????<insert?id="insertAccount"?parameterClass="Account"> 22 ????????insert?into?Account?(owner,balance)?values?(#owner#,#balance#); 23 ????</insert> 24 </sqlMap> 還要建立兩個(gè)ibatis的配置文件sql-map-config_A.xml和sql-map-config_B.xml,由于有兩個(gè)數(shù)據(jù)庫(kù),所以要建兩個(gè)。具體不列出。 編寫(xiě)spring的applicationContext.xml
??1 <?xml?version="1.0"?encoding="UTF-8"?> ??2 <beans?xmlns="http://www.springframework.org/schema/beans" ??3 ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ??4 ????xmlns:jee="http://www.springframework.org/schema/jee" ??5 ????xmlns:aop="http://www.springframework.org/schema/aop" ??6 ????xmlns:tx="http://www.springframework.org/schema/tx" ??7 ????xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans-2.0.xsd? ??8 ?????????????????????http://www.springframework.org/schema/jee?http://www.springframework.org/schema/jee/spring-jee-2.0.xsd? ??9 ?????????????????????http://www.springframework.org/schema/aop?http://www.springframework.org/schema/aop/spring-aop-2.0.xsd? ?10 ?????????????????????http://www.springframework.org/schema/tx?http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"> ?11 ?12 ????<!--?讀取數(shù)據(jù)庫(kù)連接配置文件?--> ?13 ????<bean?id="propertyConfig" ?14 ????????class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> ?15 ????????<property?name="locations"> ?16 ????????????<list> ?17 ????????????????<value>classpath:jdbc.properties</value> ?18 ????????????</list> ?19 ????????</property> ?20 ????</bean> ?21 ?22 ????<!--?數(shù)據(jù)源A(使用com.atomikos.jdbc.AtomikosDataSourceBean作為數(shù)據(jù)源)?--> ?23 ????<bean?id="datasourceA" ?24 ????????class="com.atomikos.jdbc.AtomikosDataSourceBean"?init-method="init" ?25 ????????destroy-method="close"> ?26 ????????<property?name="uniqueResourceName"> ?27 ????????????<value>mysql/db_a</value> ?28 ????????</property> ?29 ????????<property?name="xaDataSourceClassName"> ?30 ????????????<value>${jdbc.driverClassName}</value> ?31 ????????</property> ?32 ????????<property?name="poolSize"> ?33 ????????????<value>3</value> ?34 ????????</property> ?35 ?????????<property?name="xaProperties"> ?36 ?????????????<props> ?37 ?????????????????<prop?key="url">${jdbc.url}</prop> ?38 ?????????????????<prop?key="user">${jdbc.username}</prop> ?39 ?????????????????<prop?key="password">${jdbc.password}</prop> ?40 ?????????????</props> ?41 ?????????</property> ?42 ????</bean> ?43 ?44 ????<!--?數(shù)據(jù)源B(使用com.atomikos.jdbc.SimpleDataSourceBean作為數(shù)據(jù)源)?--> ?45 ????<bean?id="datasourceB" ?46 ????????class="com.atomikos.jdbc.SimpleDataSourceBean"?init-method="init" ?47 ????????destroy-method="close"> ?48 ????????<property?name="uniqueResourceName"> ?49 ????????????<value>mysql/db_b</value> ?50 ????????</property> ?51 ????????<property?name="xaDataSourceClassName"> ?52 ????????????<value>${jdbc2.driverClassName}</value> ?53 ????????</property> ?54 ????????<property?name="xaDataSourceProperties"> ?55 ????????????<value>user=${jdbc2.username};password=${jdbc2.password};url=${jdbc2.url};encoding=${jdbc2.encoding}</value> ?56 ????????</property> ?57 ????????<property?name="exclusiveConnectionMode"> ?58 ????????????<value>true</value> ?59 ????????</property> ?60 ????????<property?name="validatingQuery"> ?61 ????????????<value>SELECT?1</value> ?62 ????????</property> ?63 ????????<property?name="connectionPoolSize"> ?64 ????????????<value>3</value> ?65 ????????</property> ?66 ????</bean> ?67 ?68 ????<!--?配置atomikos的事務(wù)管理器?--> ?69 ????<bean?id="atomikosTransactionManager" ?70 ????????class="com.atomikos.icatch.jta.UserTransactionManager" ?71 ????????init-method="init"?destroy-method="close"> ?72 ????????<property?name="forceShutdown"?value="true"?/> ?73 ????</bean> ?74 ?75 ????<bean?id="atomikosUserTransaction" ?76 ????????class="com.atomikos.icatch.jta.UserTransactionImp"> ?77 ????????<property?name="transactionTimeout"?value="300"?/> ?78 ????</bean> ?79 ?80 ????<!--?spring的JTA事務(wù)管理器?--> ?81 ????<bean?id="springTransactionManager" ?82 ????????class="org.springframework.transaction.jta.JtaTransactionManager"> ?83 ????????<property?name="transactionManager" ?84 ????????????ref="atomikosTransactionManager"?/> ?85 ????????<property?name="userTransaction"?ref="atomikosUserTransaction"?/> ?86 ????</bean> ?87 ?88 ????<!--?通知配置?--> ?89 ????<tx:advice?id="txAdvice" ?90 ????????transaction-manager="springTransactionManager"> ?91 ????????<tx:attributes> ?92 ????????????<tx:method?name="delete*"?rollback-for="Exception"?/> ?93 ????????????<tx:method?name="save*"?rollback-for="Exception"?/> ?94 ????????????<tx:method?name="update*"?rollback-for="Exception"?/> ?95 ????????????<tx:method?name="*"?read-only="true" ?96 ????????????????rollback-for="Exception"?/> ?97 ????????</tx:attributes> ?98 ????</tx:advice> ?99 100 ????<!--?事務(wù)切面配置?--> 101 ????<aop:config> 102 ????????<aop:pointcut?id="serviceOperation" 103 ????????????expression="execution(*?*..service*..*(..))"?/> 104 ????????<aop:advisor?advice-ref="txAdvice" 105 ????????????pointcut-ref="serviceOperation"?/> 106 ????</aop:config> 107 108 ????<!--?根據(jù)dataSourceA和sql-map-config_A.xml創(chuàng)建一個(gè)SqlMapClientA?--> 109 ????<bean?id="sqlMapClientA" 110 ????????class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 111 ????????<property?name="dataSource"> 112 ????????????<ref?local="datasourceA"?/> 113 ????????</property> 114 ????????<property?name="configLocation"> 115 ????????????<value>classpath:/sql-map-config_A.xml</value> 116 ????????</property> 117 ????</bean> 118 119 ????<!--?根據(jù)dataSourceB和sql-map-config_B.xml創(chuàng)建一個(gè)SqlMapClientB?--> 120 ????<bean?id="sqlMapClientB" 121 ????????class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 122 ????????<property?name="dataSource"> 123 ????????????<ref?local="datasourceB"?/> 124 ????????</property> 125 ????????<property?name="configLocation"> 126 ????????????<value>classpath:/sql-map-config_B.xml</value> 127 ????????</property> 128 ????</bean> 129 130 ????<!--根據(jù)sqlMapClientA創(chuàng)建一個(gè)SqlMapClientTemplate的模版類實(shí)例sqlMapClientTemplateA--> 131 ????<bean?id="sqlMapClientTemplateA" 132 ????????class="org.springframework.orm.ibatis.SqlMapClientTemplate"> 133 ????????<property?name="sqlMapClient"?ref="sqlMapClientA"?/> 134 ????</bean> 135 136 ????<!--根據(jù)sqlMapClientB創(chuàng)建一個(gè)SqlMapClientTemplate的模版類實(shí)例sqlMapClientTemplateB--> 137 ????<bean?id="sqlMapClientTemplateB" 138 ????????class="org.springframework.orm.ibatis.SqlMapClientTemplate"> 139 ????????<property?name="sqlMapClient"?ref="sqlMapClientB"?/> 140 ????</bean> 141 142 ????<!--?配置DAO?--> 143 ????<bean?id="accountaDao"?class="com.aaron.atomikos.dao.AccountDao"> 144 ????????<property?name="sqlMapClientTemplate" 145 ????????????ref="sqlMapClientTemplateA"?/> 146 ????</bean> 147 148 ????<bean?id="accountbDao"?class="com.aaron.atomikos.dao.AccountDao"> 149 ????????<property?name="sqlMapClientTemplate" 150 ????????????ref="sqlMapClientTemplateB"?/> 151 ????</bean> 152 153 ????<!--?配置service?--> 154 ????<bean?id="accountService" 155 ????????class="com.aaron.atomikos.service.impl.AccountServiceImpl"> 156 ????????<property?name="accountaDao"?ref="accountaDao"?/> 157 ????????<property?name="accountbDao"?ref="accountbDao"?/> 158 ????</bean> 159 </beans> 在這里,我使用了兩個(gè)不同類的數(shù)據(jù)源,只是為了測(cè)試,具體配置如上。兩者設(shè)置屬性時(shí)有點(diǎn)差別,數(shù)據(jù)源com.atomikos.jdbc.AtomikosDataSourceBean中設(shè)置url,user,password屬性是用xaProperties這個(gè)屬性,它是個(gè)Properties類型,所以要用<props>標(biāo)簽分開(kāi)設(shè)置,數(shù)據(jù)源com.atomikos.jdbc.SimpleDataSourceBean中設(shè)置url,user,password屬性使用xaDataSourceProperties這個(gè)屬性,它是String類型,查看源代碼可以知道,這個(gè)類會(huì)把xaDataSourceProperties按“;”進(jìn)行分割,得到user=root,password=root等幾個(gè)字符串,再將“=”兩邊的字符串分別作為屬性名和屬性值放入一個(gè)properties變量中。有一點(diǎn)要注意,
1 <value>user=${jdbc2.username};password=${jdbc2.password};url=${jdbc2.url};encoding=${jdbc2.encoding}</value> 中的<value>和user=${jdbc2.username}之間不能換行,因?yàn)樵擃惒粫?huì)忽略換行,會(huì)把換行加上user當(dāng)成一個(gè)字符串,之后會(huì)報(bào)錯(cuò)的。
編寫(xiě)測(cè)試類AccountServiceTest
?1 package?com.aaron.atomikos.test; ?2 ?3 import?org.springframework.context.ApplicationContext; ?4 import?org.springframework.context.support.ClassPathXmlApplicationContext; ?5 import?com.aaron.atomikos.service.AccountService; ?6 import?junit.framework.Assert; ?7 import?junit.framework.TestCase; ?8 ?9 public?class?AccountServiceTest?extends?TestCase? { 10 11 ????protected?static?ApplicationContext?????????????applicationContext; 12 ????protected?static?AccountService?accountService; 13 ????static? { 14 ????????applicationContext?=?new?ClassPathXmlApplicationContext("applicationContext.xml"); 15 ????????accountService?=?(AccountService)?applicationContext.getBean("accountService"); 16 ????} 17 ???? 18 ????public?void?testTransfer() { 19 ????????int?ida?=?1; 20 ????????int?idb?=?1; 21 ????????Assert.assertNotNull(accountService.transfer(ida,?idb,?666)); 22 ????} 23 } 執(zhí)行,查看數(shù)據(jù)庫(kù),成功!若將AccountserviceImpl中的拋出異常注釋去掉,則會(huì)在兩個(gè)數(shù)據(jù)庫(kù)中回滾。
|