wKioL1Qk0QvxKmdfAADWFY98B1s037.jpg

BookShopDao接口

public interface BookShopDao {

	//根据书号获取书的单价
	public int findBookPriceByIsbn(String isbn);
	
	//更新书的库存,使书号对应的库存-1
	public void updateBookStock(String isbn);
	
	//更新用户的账户余额,是username的balance-price
	public void updateUserAccount(String username, int price);
}

BookShopDao接口的实现类

public class BookShopImpl implements BookShopDao{

	private JdbcTemplate jdbcTemplate;
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.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 s = "select stock from book_stock where isbn = ?";
		int stock = jdbcTemplate.queryForObject(s, Integer.class, isbn);
		//System.out.println("stock:" + stock);
		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 s = "select balance from account where username = ?";
		int balance = jdbcTemplate.queryForObject(s, Integer.class, username);
		if (balance < price) {
			//System.out.println("余额不足");
			throw new UserAccountException("余额不足");
		}
		String sql = "update account set balance = balance - ? where username = ?";
		jdbcTemplate.update(sql, price, username);
	}

}

BookStockException.java和UserAccountException.java为自定义的两个异常类,继承RuntimeException。

BookShopService接口

public interface BookShopService {

	public void purchase(String username, String isbn);
}

BookShopService接口的实现类

public class BookShopServiceImpl implements BookShopService{

	private BookShopDao bookShopDao;
	public void setBookShopDao(BookShopDao bookShopDao) {
		this.bookShopDao = bookShopDao;
	}
	
	@Override
	public void purchase(String username, String isbn) {
		/*
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		*/
		//获取书的单价
		int price = bookShopDao.findBookPriceByIsbn(isbn);
		//更新书的库存
		bookShopDao.updateBookStock(isbn);
		//更新用户余额
		bookShopDao.updateUserAccount(username, price);
	}

}

applicationContext.xml配置

<?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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">

	<context:component-scan base-package="com.spring"></context:component-scan>
	<!-- 导入资源文件 -->
	<context:property-placeholder location="classpath:db.properties" />
	<!-- 配置C3P0数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		
		<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
	</bean>
	
	<!-- 配置Spring的JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置bean -->
	<bean id="bookShopDao" class="com.spring.tx.xml.BookShopImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	
	<bean id="bookShopService" class="com.spring.tx.xml.BookShopServiceImpl">
		<property name="bookShopDao" ref="bookShopDao"></property>
	</bean>
	
	<bean id="cashier" class="com.spring.tx.xml.CashierImpl">
		<property name="bookShopService" ref="bookShopService"></property>
	</bean>
	
	<!-- 1.配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 2.配置事务属性 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		 <tx:attributes>
		 	<!-- 根据方法名指定事务的属性 -->
		 	<tx:method name="purchase" propagation="REQUIRES_NEW" timeout="3"/>
		 	
		 	<tx:method name="*"/>
		 	<tx:method name="get*" read-only="true"/>
		 	<tx:method name="find*" read-only="true"/>
		 	
		 </tx:attributes>
	</tx:advice>
	
	<!-- 3.配置事务切入点,以及把事务切入点和事务属性关联起来 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.spring.tx.xml.BookShopService.*(..))" id="txPointCut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
	</aop:config>
	
	<aop:config>
		<aop:pointcut expression="execution(* com.spring.tx.xml.Cashier.*(..))" id="cashierPointCut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="cashierPointCut"/>
	</aop:config>
</beans>

测试

        @Test
	public void testTransactionalPropagation() {
		cashier.checkout("umgsai", Arrays.asList("1001", "1002"));
	}