学习Spring(十) -- Spring声明式事务

准备数据

    准备两张数据表BOOKS和USERS,表结构如下:

    这张表为BOOKS表,有书名、价格和库存。

    这张表为USERS表,有用户名称和余额。

    接下来,编写买书的方法,写一个书店的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);
}

    该接口拥有三个方法:

  • 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);
	}

}

    接下来进行测试:

	@Test
	public void testBookShopService() {
		// 测试,购买Jack余额是200,购买1号图书,108。
		// 余额还剩92。
		bookShopService.purchase("Jack", 1);
	}

    这时,如果在买一本则会出错。回到Service的方法:

    首先,获得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层的代码中加入业务逻辑了。

转载于:https://my.oschina.net/u/2450666/blog/676905

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值