一、测试数据准备
1、需求
2、数据表
CREATE TABLE book ( isbn VARCHAR (50) PRIMARY KEY, book_name VARCHAR (100), price INT ) ; CREATE TABLE book_stock ( isbn VARCHAR (50) PRIMARY KEY, stock INT, CHECK (stock > 0) ) ; CREATE TABLE account ( username VARCHAR (50) PRIMARY KEY, balance INT, CHECK (balance > 0) ) ; INSERT INTO account (`username`,`balance`) VALUES ('Tom',100000); INSERT INTO account (`username`,`balance`) VALUES ('Jerry',150000); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-001','book01',100); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-002','book02',200); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-003','book03',300); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-004','book04',400); INSERT INTO book (`isbn`,`book_name`,`price`) VALUES ('ISBN-005','book05',500); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-001',1000); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-002',2000); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-003',3000); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-004',4000); INSERT INTO book_stock (`isbn`,`stock`) VALUES ('ISBN-005',5000);
二、环境搭建
在配置文件中配置数据源和 JdbcTemplate:
<context:component-scan base-package="com.njf.tx"></context:component-scan> <context:property-placeholder location="db.properties"></context:property-placeholder> <!-- 配置数据源 --> <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="jdbcUrl" value="${jdbc.jdbcUrl}"></property> <property name="driverClass" value="${jdbc.driverClass}"></property> </bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean>
三、业务代码
BookDao:
@Repository public class BookDao { @Autowired JdbcTemplate jdbcTemplate; /** * 1、减余额 * * 减去用户的余额 */ public void updateBalance(String userName, int price) { String sql = "UPDATE account SET balance = balance - ? WHERE username = ?"; int i = jdbcTemplate.update(sql,price, userName); } /** * 2. 获取某本图书的价格 * @return */ public int getPrice(String isbn) { String sql = "SELECT price FROM book WHERE isbn = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, isbn); } /** * 3. 减去对应图书的库存,每次减1 */ public void updateStock(String isbn) { String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?"; jdbcTemplate.update(sql,isbn); } }
BookService:
@Service public class BookService { @Autowired private BookDao bookDao; /** * 结账,哪个用户买了哪本书 * @param userName * @param isbn */ public void checkOut(String userName, String isbn) { bookDao.updateStock(isbn); int price = bookDao.getPrice(isbn); bookDao.updateBalance(userName, price); } }
测试:
public class TxTest { ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml"); @Test public void test() { BookService bookService = ioc.getBean(BookService.class); bookService.checkOut("Tom", "ISBN-001"); System.out.println("结账完成"); } }
四、添加事务管理
1、配置事务管理器
<!--1: 配置事务管理器让其进行事务控制--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--控制住数据源--> <property name="dataSource" ref="dataSource"></property> </bean>
2、开启基于注解的事务控制
<!-- 2: 开启基于注解的事务控制模式:依赖 tx 名称空间 --> <tx:annotation-driven transaction-manager="transactionManager" />
3、给事务方法加注解
@Transactional public void checkOut(String userName, String isbn) { bookDao.updateStock(isbn); int price = bookDao.getPrice(isbn); int i = 10 / 0; bookDao.updateBalance(userName, price); }
测试:当给加了@Transactional 方法的内部手动制造异常情况,整个操作都会回滚,满足事务的要求。