學習Spring(十) -- Spring聲明式事務

準備數據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

  • findBookPriceById:經過書的Id查找書的價格;
  • updateBookStockById:經過書的Id更新書的庫存;
  • updateUserAccountById:根據用戶名稱更新用戶的餘額

    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層的代碼中加入業務邏輯了。

相關文章
相關標籤/搜索