学习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层的代码中加入业务逻辑了。

相关文章
相关标签/搜索