准备数据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层的代码中加入业务逻辑了。