準備數據spring
準備兩張數據表BOOKS和USERS,表結構以下:sql
這張表爲BOOKS表,有書名、價格和庫存。框架
這張表爲USERS表,有用戶名稱和餘額。less
接下來,編寫買書的方法,寫一個書店的Dao接口:分佈式
package cn.net.bysoft.lesson8; public interface BookShopDao { // 根據id查詢書的價格,要買書以前須要得到書的價格。 public int findBookPriceById(int id); // 根據id更新書的庫存,購買了該圖書,庫存要減小一本。 public void updateBookStockById(int id); // 根據用戶名稱更新用戶的餘額,經過用戶的名稱,和購買的圖書的價格,減小用戶的餘額。 public void updateUserAccountById(String name, int price); }
該接口擁有三個方法:ide
Dao的實現類以下:函數
package cn.net.bysoft.lesson8; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("bookShopDao") public class BookShopDaoImpl implements BookShopDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public int findBookPriceById(int id) { // 經過書的Id查找書的價格。 String sql = "SELECT PRICE FROM BOOKS WHERE ID = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, id); } @Override public void updateBookStockById(int id) { // 驗證庫存是否足夠,經過書的Id查找到書的庫存。 String sql2 = "SELECT STOCK FROM BOOKS WHERE ID = ?"; int stock = jdbcTemplate.queryForObject(sql2, Integer.class, id); // 若是庫存爲0,則拋出異常,提示庫存不足。 if (stock == 0) throw new BookStockException("庫存不足"); // 不然將庫存減1。 String sql = "UPDATE BOOKS SET STOCK = STOCK - 1 WHERE ID = ?"; jdbcTemplate.update(sql, id); } @Override public void updateUserAccountById(String name, int price) { // 驗證餘額是否足夠 String sql2 = "SELECT BALANCE FROM USERS WHERE NAME = ?"; int balance = jdbcTemplate.queryForObject(sql2, Integer.class, name); // 若是餘額不夠要買的書的價格,則拋出異常,提示用戶餘額不足。 if (balance < price) throw new UserAccountException("用戶餘額不足"); String sql = "UPDATE USERS SET BALANCE = BALANCE - ? WHERE NAME = ?"; jdbcTemplate.update(sql, price, name); } }
接下來編寫Service的接口和類:測試
package cn.net.bysoft.lesson8; public interface BookShopService { // 購買圖書,須要傳遞的參數爲購買圖書的用戶名稱和購買的圖書Id。 public void purchase(String userName, int bookId); }
package cn.net.bysoft.lesson8; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; @Override public void purchase(String userName, int bookId) { // 獲取書的單價。 int price = bookShopDao.findBookPriceById(bookId); // 更新書的庫存。 bookShopDao.updateBookStockById(bookId); // 更新用戶的餘額。 bookShopDao.updateUserAccountById(userName, price); } }
接下來進行測試:spa
@Test public void testBookShopService() { // 測試,購買Jack餘額是200,購買1號圖書,108。 // 餘額還剩92。 bookShopService.purchase("Jack", 1); }
這時,若是在買一本則會出錯。回到Service的方法:.net
首先,得到1號書的價格,108,接着庫存減1,最後,當須要減小用戶餘額時會拋出異常,用戶餘額還有92,不夠108。可是庫存卻減小了。測試一下,再次執行這個函數,庫存減小了可是用戶餘額沒有減小:
讓咱們把分佈式事務加上,在Service的purchase方法上加入@Transactional註解:
package cn.net.bysoft.lesson8; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; @Transactional(propagation=Propagation.REQUIRED) @Override public void purchase(String userName, int bookId) { // 獲取書的單價。 int price = bookShopDao.findBookPriceById(bookId); // 更新書的庫存。 bookShopDao.updateBookStockById(bookId); // 更新用戶的餘額。 bookShopDao.updateUserAccountById(userName, price); } }
配置文件以下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" 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-4.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd "> <!-- 基於屬性文件配置 --> <context:property-placeholder location="classpath:db.properties" /> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="user" value="${user}"></property> <property name="password" value="${password}"></property> <property name="driverClass" value="${driverClass}"></property> <property name="jdbcUrl" value="${jdbcUrl}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <context:component-scan base-package="cn.net.bysoft.lesson8"> </context:component-scan> <!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> </beans>
在進行測試:
結果如上圖,程序拋出異常,庫存和餘額並無不正確。
能夠從XML配置文件中看出,已經將datasource託管給了spring框架管理,spring框架來肯定什麼時候何地去commit。
這樣,就不再用在dao層的代碼中加入業務邏輯了。