声明式事务
事务分类
编程式事务
声明式事务
Connection connection = JdbcUtils.getConnection();
try {//1. 先设置事务不要自动提交
connection.setAutoCommint(false);
//2. 进行各种 crud
//多个表的修改,添加 ,删除
//3. 提交
connection.commit();
} catch (Exception e) {
//4. 回滚
conection.rollback();
}
声明式事务
需求
我们需要去处理用户购买商品的业务逻辑
分析:当一个用户要去购买商品应该包含三个步骤
- 通过商品 id 获取价格。
- 购买商品(某人购买商品,修改用户的余额) 。
- 修改库存量 。
- 其实大家可以看到,这时,我们需要涉及到三张表商品表,用户表,商品存量表。 应该使用事务处理。
传统的编程式事务分析
使用传统的编程事务来处理,将代码写到一起[缺点: 代码冗余,效率低,不利于扩展, 优 点是简单,好理解]。
使用 Spring 的声明式事务处理, 可以将上面三个子步骤分别写成一个方法,然后统一 管理[这个是 Spring 很牛的地方,在开发使用的很多,优点是无代码冗余,效率高,扩展方便, 缺点是理解较困难]==> 底层使用 AOP (动态代理+动态绑定+反射+注解)。
声明式事务使用
先创建商品系统的数据库和表
-- 演示声明式事务创建的表
CREATE TABLE `user_account`(
user_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
user_name VARCHAR(32) NOT NULL DEFAULT '',
money DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8;
INSERT INTO `user_account` VALUES(NULL,'张三', 1000);
INSERT INTO `user_account` VALUES(NULL,'李四', 2000);
CREATE TABLE `goods`(
goods_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
goods_name VARCHAR(32) NOT NULL DEFAULT '',
price DOUBLE NOT NULL DEFAULT 0.0
)CHARSET=utf8 ;
INSERT INTO `goods` VALUES(NULL,'小风扇', 10.00);
INSERT INTO `goods` VALUES(NULL,'小台灯', 12.00);
INSERT INTO `goods` VALUES(NULL,'可口可乐', 3.00);
CREATE TABLE `goods_amount`(
goods_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
goods_num INT UNSIGNED DEFAULT 0
)CHARSET=utf8 ;
INSERT INTO `goods_amount` VALUES(1,200);
INSERT INTO `goods_amount` VALUES(2,20);
INSERT INTO `goods_amount` VALUES(3,15);
创建GoodsDao
package com.lzw.spring.tx.dao;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
* @author LiAng
*/
@Repository
public class GoodsDao {
@Resource
private JdbcTemplate jdbcTemplate;
/**
* 根据商品id,返回对应的价格
* @param id
* @return
*/
public Float queryPriceById(Integer id) {
String sql = "SELECT price From goods Where goods_id=?";
Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
return price;
}
/**
* 修改用户的余额 [减少用户余额]
* @param user_id
* @param money
*/
public void updateBalance(Integer user_id, Float money) {
String sql = "UPDATE user_account SET money=money-? Where user_id=?";
jdbcTemplate.update(sql, money, user_id);
}
/**
* 修改商品库存 [减少]
* @param goods_id
* @param amount
*/
public void updateAmount(Integer goods_id, int amount){
String sql = "UPDATE goods_amount SET goods_num=goods_num-? Where goods_id=?";
jdbcTemplate.update(sql, amount , goods_id);
}
}
创建src\tx_ioc.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置要扫描的包-->
<context:component-scan base-package="com.lzw.spring.tx.dao"/>
<!--引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象-DataSoruce-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--给数据源对象配置属性值-->
<property name="user" value="${jdbc.userName}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置JdbcTemplate对象-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--给JdbcTemplate对象配置dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
创建TxTest 进行测试
package com.lzw.spring.tx;
import com.lzw.spring.tx.dao.GoodsDao;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author LiAng
*/
public class TxTest {
@Test
public void queryPriceByIdTest(){
//获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsDao goodsDao = ioc.getBean(GoodsDao.class);
Float price = goodsDao.queryPriceById(1);
System.out.println("id=1 的price=" + price);
}
@Test
public void updateBalance() {
//获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsDao goodsDao = ioc.getBean(GoodsDao.class);
goodsDao.updateBalance(1, 1.0F);
System.out.println("减少用户余额成功~");
}
@Test
public void updateAmount() {
//获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsDao goodsDao = ioc.getBean(GoodsDao.class);
goodsDao.updateAmount(1, 1);
System.out.println("减少库存成功...");
}
}
创建GoodsService
package com.lzw.spring.tx.service;
import com.lzw.spring.tx.dao.GoodsDao;
import org.springframework.stereotype.Service;
/**
* @author LiAng
*/
@Service //将 GoodsService对象注入到spring容器
public class GoodsService {
//
@Resource
private GoodsDao goodsDao;
//编写一个方法,完成用户购买商品的业务, 这里主要是讲解事务管理
/**
* @param userId 用户id
* @param goodsId 商品id
* @param amount 购买数量
*/
public void buyGoods(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById(userId);
//2. 减少用户的余额
goodsDao.updateBalance(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount(goodsId, amount);
System.out.println("用户购买成功~");
}
}
修改tx_ioc.xml
<context:component-scan base-package="com.lzw.spring.tx.service"/>
修改GoodsDao,让操作发生异常
/**
* 修改商品库存 [减少]
* @param goods_id
* @param amount
*/
public void updateAmount(Integer goods_id, int amount){
String sql = "UPDATEX goods_amount SET goods_num=goods_num-? Where goods_id=?";
jdbcTemplate.update(sql, amount , goods_id);
}
修改TxTest 进行测试
验证不使用事务就会出现数据不一致现象。
//测试用户购买商品业务
@Test
public void buyGoodsTest() {
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsService goodsService = ioc.getBean(GoodsService.class);
goodsService.buyGoods(1,1,1);
}
修改GoodsService
加入声明式事务注解。
/**
* @Transactional 注解解读
* 1. 使用@Transactional 可以进行声明式事务控制
* 2. 即将标识的方法中的,对数据库的操作作为一个事务管理
* 3. @Transactional 底层使用的仍然是AOP机制
* 4. 底层是使用动态代理对象来调用buyGoodsByTx
* 5. 在执行buyGoodsByTx() 方法 先调用 事务管理器的 doBegin() , 调用 buyGoodsByTx()
* 如果执行没有发生异常,则调用 事务管理器的 doCommit(), 如果发生异常 调用事务管理器的 doRollback()
* @param userId
* @param goodsId
* @param amount
*/
@Transactional
public void buyGoodsByTx(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById(userId);
//2. 减少用户的余额
goodsDao.updateBalance(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount(goodsId, amount);
System.out.println("用户购买成功~");
}
修改tx_ioc.xml
<!--配置事务管理器-对象
1. DataSourceTransactionManager 这个对象是进行事务管理-debug源码
2. 一定要配置数据源属性,这样指定该事务管理器 是对哪个数据源进行事务控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置启动基于注解的声明式事务管理功能-->
<tx:annotation-driven transaction-manager="transactionManager"/>
修改TxTest 进行测试
//测试用户购买商品业务
@Test
public void buyGoodsByTxTest() {
//获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsService goodsService = ioc.getBean(GoodsService.class);
goodsService.buyGoodsByTx(1, 1, 1);//这里我们调用的是进行了事务声明的方法
}
声明式事务机制-Debug
doBegin
protected void doBegin(Object transaction, TransactionDefinition definition)中

doCommit
protected void doCommit(DefaultTransactionStatus status)中

doRollback
protected void doRollback(DefaultTransactionStatus status)中

事务的传播机制
事务的传播机制说明
当有多个事务处理并存时,如何控制?
比如用户去购买两次商品(使用不同的方法),每个方法都是一个事务,那么如何控制呢?
这个就是事务的传播机制,看一个具体的案例(如图)
事务传播机制种类
事务传播的属性/种类一览图
传播属性 | 描述 |
---|---|
REQUIRED | 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行 |
REQUIREDS_NEW | 当前的方法必须启动新事务,并在它自己的事务内运行.如果有事务正在运行,应该将它挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行。否则它可以不运行在事务中 |
NOT_SUPPORTED | 当前的方法不应该运行在事务中,如果有运行的事务,将它挂起 |
MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常 |
NEVER | 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。否则,就启动一个新的事务,并在它自己的事务内运行 |
事务传播的属性/种类机制分析
重点分析了 REQUIRED 和 REQUIRED_NEW 两种事务传播属性


REQUIRES_NEW 和 REQUIRED 在处理事务的策略
- 如果设置为 REQUIRES_NEW,buyGoods2 如果错误,不会影响到 buyGoods()反之亦然,即它们的事务是独立的。
- 如果设置为 REQUIRED,buyGoods2 和 buyGoods 是一个整体,只要有方法的事务错误,那么两个方法都不会执行成功。
事务的传播机制的设置方法
@Transactional(propagation= Propagation.REQUIRES_NEW)
public void buyGoodsByTx(int userId, int goodsId, int amount)
事务的传播机制应用实例
修改 GoodsDao
/**
* 根据商品id,返回对应的价格
* @param id
* @return
*/
public Float queryPriceById2(Integer id) {
String sql = "SELECT price From goods Where goods_id=?";
Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
return price;
}
/**
* 修改用户的余额 [减少用户余额]
* @param user_id
* @param money
*/
public void updateBalance2(Integer user_id, Float money) {
String sql = "UPDATE user_account SET money=money-? Where user_id=?";
jdbcTemplate.update(sql, money, user_id);
}
/**
* 修改商品库存 [减少]
* @param goods_id
* @param amount
*/
public void updateAmount2(Integer goods_id, int amount){
String sql = "UPDATE goods_amount SET goods_num=goods_num-? Where goods_id=?";
jdbcTemplate.update(sql, amount , goods_id);
}
修改 GoodsService
增加 buyGoodsByTx2(),使用默认的传播机制
package com.lzw.spring.tx.service;
import com.lzw.spring.tx.dao.GoodsDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* @author LiAng
*/
@Service //将 GoodsService对象注入到spring容器
public class GoodsService {
//定义属性GoodsDao
@Resource
private GoodsDao goodsDao;
//编写一个方法,完成用户购买商品的业务, 这里主要是讲解事务管理
/**
* @param userId 用户id
* @param goodsId 商品id
* @param amount 购买数量
*/
public void buyGoods(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById(goodsId);
//2. 减少用户的余额
goodsDao.updateBalance(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount(goodsId, amount);
System.out.println("用户购买成功~");
}
@Transactional
public void buyGoodsByTx(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById(userId);
//2. 减少用户的余额
goodsDao.updateBalance(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount(goodsId, amount);
System.out.println("用户购买成功~");
}
/**
* 这个方法是第二套进行商品购买的方法
* @param userId
* @param goodsId
* @param amount
*/
@Transactional
public void buyGoodsByTx2(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById2(userId);
//2. 减少用户的余额
goodsDao.updateBalance2(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount2(goodsId, amount);
System.out.println("用户购买成功~");
}
}
创建 MultiplyService
package com.lzw.spring.tx.service;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* @author LiAng
*/
@Service
public class MultiplyService {
@Resource
private GoodsService goodsService;
/**
* 1. multiBuyGoodsByTx 方法中 有两次购买商品操作
* 2. buyGoodsByTx 和 buyGoodsByTx2 都是声明式事务
* 3. 当前 buyGoodsByTx 和 buyGoodsByTx2 使用的传播属性是默认的 REQUIRED
* [会当做整体事务管理,比如 buyGoodsByTx 成功,但是buyGoodsByTx2失败,
* 会造成整个事务失败,即会回滚buyGoodsByTx]
*/
@Transactional
public void multiBuyGoodsByTx(){
goodsService.buyGoodsByTx(1,1,1);
goodsService.buyGoodsByTx2(1,1,1);
}
}
修改 GoodsDao
public void updateAmount2(Integer goods_id, int amount){
String sql = "UPDATEX goods_amount SET goods_num=goods_num-? Where goods_id=?";
jdbcTemplate.update(sql, amount , goods_id);
}
修改 TxTest 并测试
//测试事务传播机制
@Test
public void multiBuyGoodsByTxTest(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
MultiplyService multiplyService = ioc.getBean(MultiplyService.class);
multiplyService.multiBuyGoodsByTx();
}
可以验证:为 REQUIRED buyGoodsByTx 和 buyGoodsByTx2 是整体,只要有方法的事务错误,那么两个方法都不会执行成功。
修改 GoodsService
更改传播机制
@Transactional(propagation= Propagation.REQUIRES_NEW)
public void buyGoodsByTx(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById(userId);
//2. 减少用户的余额
goodsDao.updateBalance(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount(goodsId, amount);
System.out.println("用户购买成功~");
}
@Transactional(propagation= Propagation.REQUIRES_NEW)
public void buyGoodsByTx2(int userId, int goodsId, int amount) {
//输出购买的相关信息
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
//1.得到商品的价格
Float price = goodsDao.queryPriceById2(userId);
//2. 减少用户的余额
goodsDao.updateBalance2(userId, price * amount);
//3. 减少库存量
goodsDao.updateAmount2(goodsId, amount);
System.out.println("用户购买成功~");
}
再次进行测试
可以验证:设置为 REQUIRES_NEW buyGoodsByTx 如果错误,不会影响到 buyGoodsByTx02()反之亦然,也就 是说它们的事务是独立的。
事务的隔离级别

事务隔离级别说明
- 默认的隔离级别,就是 mysql 数据库默认的隔离级别一般为 REPEATABLE_READ。
- 看源码可知 Isolation.DEFAULT 是 :Use the default isolation level of the underlying datastore。
- 查看数据库默认的隔离级别 SELECT @@global.tx_isolation。
事务隔离级别的设置和测试
修改 GoodsService
/**
* 1. 在默认情况下,声明式事务的隔离级别是 REPEATABLE_READ
*/
@Transactional
public void buyGoodsByTxISOLATION(){
//查询两次商品价格
Float price = goodsDao.queryPriceById(1);
System.out.println("第一次查询的价格 = " + price);
Float price2 = goodsDao.queryPriceById(1);
System.out.println("第二次查询的价格 = " + price2);
}

修改 TxTest
//测试声明式事务的隔离级别
@Test
public void buyGoodsByTxISOLATIONTest(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsService goodsService = ioc.getBean(GoodsService.class);
goodsService.buyGoodsByTxISOLATION();
}
断点调试时走到断点处,执行sql语句
update goods set price=8 where goods_id=1

测试结果
在默认隔离级别(REPEATABLE_READ)下,两次读取的价格一样。
修改 GoodsService
测试 READ_COMMITTED 隔离级别情况
/**
* 1. 在默认情况下,声明式事务的隔离级别是 REPEATABLE_READ
* 2. 我们将buyGoodsByTxISOLATION 的隔离界别设置为 Isolation.READ_COMMITTED
* ,表示只要是提交的数据,在当前事务是可以读取最新数据的。
*/
@Transactional(isolation = Isolation.READ_COMMITTED)
public void buyGoodsByTxISOLATION(){
//查询两次商品价格
Float price = goodsDao.queryPriceById(1);
System.out.println("第一次查询的价格 = " + price);
Float price2 = goodsDao.queryPriceById(1);
System.out.println("第二次查询的价格 = " + price2);
}
继续使用断点调试

断点调试时走到断点处,执行sql语句
update goods set price=10 where goods_id=1
测试结果
在 READ_COMMITTED 隔离级别下,两次读取的价格不一样。
事务的超时回滚
介绍
- 如果一个事务执行的时间超过某个时间限制,就让该事务回滚。
- 可以通过设置事务超时回顾来实现。
修改 GoodsService
/**
* 1. @Transactional(timeout = 2)
* 2. timeout = 2 表示 buyGoodsByTxTimeout 如果执行时间超过了2秒
* ,该事务就进行回滚
* 3. 如果没有设置 timeout,默认为-1,表示使用事务的默认超时时间,或者不支持
*/
@Transactional(timeout = 2)
public void buyGoodsByTxTimeout(int userId, int goodsId, int amount){
System.out.println("用户购买信息 userId=" + userId + " goodsId=" + goodsId + " 购买数量=" + amount);
Float price = goodsDao.queryPriceById(userId);
goodsDao.updateBalance(userId, price * amount);
//模拟超时
System.out.println("==========计时开始==========");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========4秒结束==========");
goodsDao.updateAmount(goodsId, amount);
System.out.println("用户购买成功~");
}
修改 TxTest 测试
//测试 timeout 属性
@Test
public void buyGoodsByTxTimeoutTest(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("tx_ioc.xml");
GoodsService goodsService = ioc.getBean(GoodsService.class);
goodsService.buyGoodsByTxTimeout(1,1,1);
}

作业
要求
模拟一个用户银行转账购买淘宝商品的的业务模块,数据表/dao/service 自己设计。使用注解完成。
seller[卖家] buyer[买家] goods[商品表[库存量]] taoBao[提取入账成交额的 10%]
代码实现
创建 TaoBaoGoodsDao
package com.lzw.spring.tx.homework.dao;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
/**
* @author LiAng
*/
@Repository //将TaoBaoGoodsDao-对象注入到spring容器
public class TaoBaoGoodsDao {
@Resource
private JdbcTemplate jdbcTemplate;
/**
* 根据商品id,返回对应的价格
* @param id
* @return
*/
public Float queryPriceById(Integer id) {
String sql = "SELECT price From goods_taobao Where goods_id=?";
Float price = jdbcTemplate.queryForObject(sql, Float.class, id);
return price;
}
/**
* 修改用户的余额 [减少用户余额]
* @param buyer_id
* @param money
*/
public void updateBalance(Integer buyer_id, Float money) {
String sql = "UPDATE buyer SET money=money-? Where buyer_id=?";
jdbcTemplate.update(sql, money, buyer_id);
}
/**
* 修改商家的余额
* @param seller_id
* @param money
*/
public void updateSellerBalance(Integer seller_id, Float money){
String sql = "UPDATE seller SET money=money+? Where seller_id=?";
jdbcTemplate.update(sql, money*0.9, seller_id);
}
/**
* 修改商品库存 [减少]
* @param goods_id
* @param amount
*/
public void updateAmount(Integer goods_id, int amount){
String sql = "UPDATE goods_taobao SET goods_num=goods_num-? Where goods_id=?";
jdbcTemplate.update(sql, amount , goods_id);
}
/**
* 添加taobao记录
* @param seller_id
* @param buyer_id
* @param goods_id
* @param money
*/
public void updateTaobao(Integer seller_id, Integer buyer_id, Integer goods_id, Float money){
String sql = "INSERT INTO `taobao` VALUES(NULL,?,?,?,?)";
jdbcTemplate.update(sql, seller_id, buyer_id, goods_id, money*0.1);
}
}
创建 TaoBaoGoodsService
package com.lzw.spring.tx.homework.service;
import com.lzw.spring.tx.homework.dao.TaoBaoGoodsDao;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
* @author LiAng
*/
@Service
public class TaoBaoGoodsService {
@Resource
private TaoBaoGoodsDao taoBaoGoodsDao;
@Transactional
public void buyGoods(int buyer_id, int seller_id, int goods_id, int amount){
System.out.println("=====================用户购买信息=====================");
System.out.println("买家id: " + buyer_id);
System.out.println("卖家id: " + seller_id);
System.out.println("商品id: " + buyer_id);
System.out.println("数量id: " + buyer_id);
//1.得到商品单价
Float price = taoBaoGoodsDao.queryPriceById(goods_id);
System.out.println("商品单价:" + price);
System.out.println("======================================================");
//2.修改用户的余额
taoBaoGoodsDao.updateBalance(buyer_id, price*amount);
//3.修改商家的余额
taoBaoGoodsDao.updateSellerBalance(seller_id, price*amount);
//4.修改商品库存
taoBaoGoodsDao.updateAmount(goods_id, amount);
//5.修改taobao表
taoBaoGoodsDao.updateTaobao(seller_id, buyer_id, goods_id, price*amount);
System.out.println("用户购买成功~");
}
}
配置 shopping_ioc.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--配置要扫描的包-->
<context:component-scan base-package="com.lzw.spring.tx.homework.dao"/>
<context:component-scan base-package="com.lzw.spring.tx.homework.service"/>
<!--引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据源对象-DataSoruce-->
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource">
<!--给数据源对象配置属性值-->
<property name="user" value="${jdbc.userName}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driverClass" value="${jdbc.driverClass}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
</bean>
<!--配置JdbcTemplate对象-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<!--给JdbcTemplate对象配置dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-对象-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置启动基于注解的声明式事务管理功能-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
创建 Test 测试
package com.lzw.spring.tx.homework;
import com.lzw.spring.tx.homework.service.TaoBaoGoodsService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author LiAng
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("shopping_ioc.xml");
TaoBaoGoodsService taoBaoGoodsService = ioc.getBean(TaoBaoGoodsService.class);
taoBaoGoodsService.buyGoods(1,1,1,1);
}
}
运行结果
