book(isbn, book_name, price) account(username, balance) book_stock(isbn, stock)
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <import resource="applicationContext-db.xml" /> ////自動掃描包 <context:component-scan base-package="com.springinaction.transaction"> </context:component-scan> <tx:annotation-driven transaction-manager="txManager"/> ///JDBC事務管理類 <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
BookShopDaohtml
package com.springinaction.transaction; public interface BookShopDao { // 根據書號獲取書的單價 public int findBookPriceByIsbn(String isbn); // 更新書的庫存,使書號對應的庫存-1 public void updateBookStock(String isbn); // 更新用戶的帳戶餘額:account的balance-price public void updateUserAccount(String username, int price); }
BookShopDaoImpljava
package com.springinaction.transaction; 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 findBookPriceByIsbn(String isbn) { String sql = "SELECT price FROM book WHERE isbn = ?"; return JdbcTemplate.queryForObject(sql, Integer.class, isbn); } @Override public void updateBookStock(String isbn) { //檢查書的庫存是否足夠,若不夠,則拋出異常 String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?"; int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn); if (stock == 0) { throw new BookStockException("庫存不足!"); } String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?"; JdbcTemplate.update(sql, isbn); } @Override public void updateUserAccount(String username, int price) { //檢查餘額是否不足,若不足,則拋出異常 String sql2 = "SELECT balance FROM account WHERE username = ?"; int balance = JdbcTemplate.queryForObject(sql2, Integer.class, username); if (balance < price) { throw new UserAccountException("餘額不足!"); } String sql = "UPDATE account SET balance = balance - ? WHERE username = ?"; JdbcTemplate.update(sql, price, username); } }
BookShopServicespring
package com.springinaction.transaction; public interface BookShopService { public void purchase(String username, String isbn); }
BookShopServiceImplsql
package com.springinaction.transaction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; /** * 1.添加事務註解 * 使用propagation 指定事務的傳播行爲,即當前的事務方法被另一個事務方法調用時如何使用事務。 * 默認取值爲REQUIRED,即便用調用方法的事務 * REQUIRES_NEW:使用本身的事務,調用的事務方法的事務被掛起。 * * 2.使用isolation 指定事務的隔離級別,最經常使用的取值爲READ_COMMITTED * 3.默認狀況下 Spring 的聲明式事務對全部的運行時異常進行回滾,也能夠經過對應的屬性進行設置。一般狀況下,默認值便可。 * 4.使用readOnly 指定事務是否爲只讀。 表示這個事務只讀取數據但不更新數據,這樣能夠幫助數據庫引擎優化事務。若真的是一個只讀取數據庫值得方法,應設置readOnly=true * 5.使用timeOut 指定強制回滾以前事務能夠佔用的時間。 */ @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, noRollbackFor={UserAccountException.class}, readOnly=true, timeout=3) @Override public void purchase(String username, String isbn) { //1.獲取書的單價 int price = bookShopDao.findBookPriceByIsbn(isbn); //2.更新書的庫存 bookShopDao.updateBookStock(isbn); //3.更新用戶餘額 bookShopDao.updateUserAccount(username, price); } }
Cashier數據庫
package com.springinaction.transaction; import java.util.List; public interface Cashier { public void checkout(String username, List<String>isbns); }
CashierImpl:CashierImpl.checkout和bookShopService.purchase聯合測試了事務的傳播行爲app
package com.springinaction.transaction; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("cashier") public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional @Override public void checkout(String username, List<String> isbns) { for(String isbn : isbns) { bookShopService.purchase(username, isbn); } } }
BookStockExceptionide
package com.springinaction.transaction; public class BookStockException extends RuntimeException { private static final long serialVersionUID = 1L; public BookStockException() { super(); // TODO Auto-generated constructor stub } public BookStockException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { super(arg0, arg1, arg2, arg3); // TODO Auto-generated constructor stub } public BookStockException(String arg0, Throwable arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } public BookStockException(String arg0) { super(arg0); // TODO Auto-generated constructor stub } public BookStockException(Throwable arg0) { super(arg0); // TODO Auto-generated constructor stub } }
UserAccountException測試
package com.springinaction.transaction; public class UserAccountException extends RuntimeException { private static final long serialVersionUID = 1L; public UserAccountException() { super(); // TODO Auto-generated constructor stub } public UserAccountException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { super(arg0, arg1, arg2, arg3); // TODO Auto-generated constructor stub } public UserAccountException(String arg0, Throwable arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub } public UserAccountException(String arg0) { super(arg0); // TODO Auto-generated constructor stub } public UserAccountException(Throwable arg0) { super(arg0); // TODO Auto-generated constructor stub } }
測試類優化
package com.springinaction.transaction; import java.util.Arrays; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class SpringTransitionTest { private ApplicationContext ctx = null; private BookShopDao bookShopDao = null; private BookShopService bookShopService = null; private Cashier cashier = null; { ctx = new ClassPathXmlApplicationContext("config/transaction.xml"); bookShopDao = ctx.getBean(BookShopDao.class); bookShopService = ctx.getBean(BookShopService.class); cashier = ctx.getBean(Cashier.class); } @Test public void testBookShopDaoFindPriceByIsbn() { System.out.println(bookShopDao.findBookPriceByIsbn("1001")); } @Test public void testBookShopDaoUpdateBookStock(){ bookShopDao.updateBookStock("1001"); } @Test public void testBookShopDaoUpdateUserAccount(){ bookShopDao.updateUserAccount("AA", 100); } @Test public void testBookShopService(){ bookShopService.purchase("AA", "1001"); } @Test public void testTransactionPropagation(){ cashier.checkout("AA", Arrays.asList("1001", "1002")); } }
事務的處理過程:首先 聲明瞭 事務管理器的實現類,id="transactionManager"。經過事務的註解,聲明事務的屬性。TransactionDefinition。對應職位狀態的查詢,經過 TransactionStatus。code