Spring基于XML实现事务管理

Spring 声明式事务管理是通过 AOP 实现的,其本质是对方法前后进行拦截,然后在目标方法开始之前创建(或加入)一个事务,在执行完目标方法后,根据执行情况提交或者回滚事务。

声明式事务最大的优点就是对业务代码的侵入性低,可以将业务代码和事务管理代码很好地进行解耦。

Spring 实现声明式事务管理主要有 2 种方式:

  • 基于 XML 方式的声明式事务管理。
  • 通过 Annotation 注解方式的事务管理。

下面介绍如何通过 XML 的方式实现声明式事务管理,步骤如下。

1. 引入 tx 命名空间

Spring 提供了一个 tx 命名空间,借助它可以极大地简化 Spring 中的声明式事务的配置。

想要使用 tx 命名空间,第一步就是要在 XML 配置文件中添加 tx 命名空间的约束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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: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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">

**注意:**由于 Spring 提供的声明式事务管理是依赖于 Spring AOP 实现的,因此我们在 XML 配置文件中还应该添加与 aop 命名空间相关的配置。

2. 配置事务管理器

接下来,我们就需要借助数据源配置,定义相应的事务管理器实现(PlatformTransactionManager 接口的实现类)的 Bean,配置内容如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!--配置数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!--数据库连接地址-->
<property name="url" value="xxx"/>
<!--数据库的用户名-->
<property name="username" value="xxx"/>
<!--数据库的密码-->
<property name="password" value="xxx"/>
<!--数据库驱动-->
<property name="driverClassName" value="xxx"/>
</bean>

<!--配置事务管理器,以 JDBC 为例-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>

在以上配置中,配置的事务管理器实现为 DataSourceTransactionManager,即为 JDBC 和 iBatis 提供的 PlatformTransactionManager 接口实现。

3. 配置事务通知

在 Spring 的 XML 配置文件中配置事务通知,指定事务作用的方法以及所需的事务属性。

1
2
3
4
5
6
7
<!--配置通知-->
<tx:advice id="tx-advice" transaction-manager="transactionManager">
<!--配置事务参数-->
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>
</tx:attributes>
</tx:advice>

事务管理器配置

当我们使用 tx:advice 来声明事务时,需要通过 transaction-manager 参数来定义一个事务管理器,这个参数的取值默认为 transactionManager。

如果我们自己设置的事务管理器(第 2 步中设置的事务管理器 id)恰好与默认值相同,则可以省略对改参数的配置。

1
2
3
4
5
6
<tx:advice id="tx-advice" >
<!--配置事务参数-->
<tx:attributes>
<tx:method name="create*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>
</tx:attributes>
</tx:advice>

但如果我们自己设置的事务管理器 id 与默认值不同,则必须手动在 tx:advice 元素中通过 transaction-manager 参数指定。

事务属性配置

对于tx:advice 来说,事务属性是被定义在tx:attributes 中的,该元素可以包含一个或多个 tx:method 元素。

tx:method 元素包含多个属性参数,可以为某个或某些指定的方法(name 属性定义的方法)定义事务属性,如下表所示。

事务属性 说明
propagation 指定事务的传播行为。
isolation 指定事务的隔离级别。
read-only 指定是否为只读事务。
timeout 表示超时时间,单位为“秒”;声明的事务在指定的超时时间后,自动回滚,避免事务长时间不提交会回滚导致的数据库资源的占用。
rollback-for 指定事务对于那些类型的异常应当回滚,而不提交。
no-rollback-for 指定事务对于那些异常应当继续运行,而不回滚。

4. 配置切点切面

tx:advice 元素只是定义了一个 AOP 通知,它并不是一个完整的事务性切面。我们在 tx:advice 元素中并没有定义哪些 Bean 应该被通知,因此我们需要一个切点来做这件事。

在 Spring 的 XML 配置中,我们可以利用 Spring AOP 技术将事务通知(tx-advice)和切点配置到切面中,配置内容如下。

1
2
3
4
5
6
7
<!--配置切点和切面-->
<aop:config>
<!--配置切点-->
<aop:pointcut id="tx-pt" expression="execution(* net.biancheng.c.service.impl.OrderServiceImpl.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="tx-advice" pointcut-ref="tx-pt"></aop:advisor>
</aop:config>

在以上配置中用到了 aop 命名空间,这就是我们为什么在给工程导入依赖时要引入 spring-aop 等 Jar 包的原因。

示例

下面,我们就通过一个实例来演示下如何通过 XML 配置事务管理,步骤如下。

1. 在 MySQL 数据库中新建一个名为 spring-tx-db 的数据库实例,并在这个数据库中执行以下 SQL 语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`user_id` bigint DEFAULT NULL COMMENT '用户id',
`total` decimal(10,0) DEFAULT NULL COMMENT '总额度',
`used` decimal(10,0) DEFAULT NULL COMMENT '已用余额',
`residue` decimal(10,0) DEFAULT '0' COMMENT '剩余可用额度',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `account` VALUES ('1', '1', '1000', '0', '1000');

DROP TABLE IF EXISTS `order`;
CREATE TABLE `order` (
`id` bigint NOT NULL AUTO_INCREMENT,
`order_id` varchar(200) NOT NULL,
`user_id` varchar(200) NOT NULL COMMENT '用户id',
`product_id` varchar(200) NOT NULL COMMENT '产品id',
`count` int DEFAULT NULL COMMENT '数量',
`money` decimal(11,0) DEFAULT NULL COMMENT '金额',
`status` int DEFAULT NULL COMMENT '订单状态:0:创建中;1:已完结',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `storage`;
CREATE TABLE `storage` (
`id` bigint NOT NULL AUTO_INCREMENT,
`product_id` bigint DEFAULT NULL COMMENT '产品id',
`total` int DEFAULT NULL COMMENT '总库存',
`used` int DEFAULT NULL COMMENT '已用库存',
`residue` int DEFAULT NULL COMMENT '剩余库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `storage` VALUES ('1', '1', '100', '0', '100');

通过以上 SQL 语句,我们共创建三张数据库表:order(订单表)、storage(商品库存表)、account(用户账户表)。

2.新建一个名为 my-spring-tx-demo 的 Java 工程,并将以下依赖项导入到工程中。

  • spring-beans-5.3.13.RELEASE.jar
  • spring-context-5.3.13.RELEASE.jar
  • spring-core-5.3.13.RELEASE.jar
  • spring-expression-5.3.13.RELEASE.jar
  • commons-logging-1.2.jar
  • spring-jdbc-5.3.13.RELEASE.jar
  • spring-tx-5.3.13.RELEASE.jar
  • spring-aop-5.3.13.jar
  • mysql-connector-java-8.0.23.jar
  • aspectjweaver-1.9.7.jar
  • spring-aspects-5.3.13.jar

3.在 net.biancheng.c.entity 包下,创建一个名为 Order 的实体类,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package net.biancheng.c.entity;

import java.math.BigDecimal;

public class Order {
//自增 id
private Long id;
//订单 id
private String orderId;
//用户 id
private String userId;
//商品 id
private String productId;
//订单商品数量
private Integer count;
//订单金额
private BigDecimal money;
//订单状态
private Integer status;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getOrderId() {
return orderId;
}

public void setOrderId(String orderId) {
this.orderId = orderId;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getProductId() {
return productId;
}

public void setProductId(String productId) {
this.productId = productId;
}

public Integer getCount() {
return count;
}

public void setCount(Integer count) {
this.count = count;
}

public BigDecimal getMoney() {
return money;
}

public void setMoney(BigDecimal money) {
this.money = money;
}

public Integer getStatus() {
return status;
}

public void setStatus(Integer status) {
this.status = status;
}
}

4.在 net.biancheng.c.entity 包下,创建一个名为 Account 的实体类,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package net.biancheng.c.entity;

import java.math.BigDecimal;

public class Order {
//自增 id
private Long id;
//订单 id
private String orderId;
//用户 id
private String userId;
//商品 id
private String productId;
//订单商品数量
private Integer count;
//订单金额
private BigDecimal money;
//订单状态
private Integer status;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getOrderId() {
return orderId;
}

public void setOrderId(String orderId) {
this.orderId = orderId;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

public String getProductId() {
return productId;
}

public void setProductId(String productId) {
this.productId = productId;
}

public Integer getCount() {
return count;
}

public void setCount(Integer count) {
this.count = count;
}

public BigDecimal getMoney() {
return money;
}

public void setMoney(BigDecimal money) {
this.money = money;
}

public Integer getStatus() {
return status;
}

public void setStatus(Integer status) {
this.status = status;
}
}

5.在 net.biancheng.c.entity 包下,创建一个名为 Storage 的实体类,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package net.biancheng.c.dao;

import net.biancheng.c.entity.Order;

public interface OrderDao {
/**
* 创建订单
* @param order
* @return
*/
int createOrder(Order order);

/**
* 修改订单状态
* 将订单状态从未完成(0)修改为已完成(1)
* @param orderId
* @param status
*/
void updateOrderStatus(String orderId, Integer status);
}

6.在 net.biancheng.net.dao 包下,创建一个名为 OrderDao 的接口,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package net.biancheng.c.dao;

import net.biancheng.c.entity.Account;

import java.math.BigDecimal;

public interface AccountDao {
/**
* 根据用户查询账户金额
* @param userId
* @return
*/
Account selectByUserId(String userId);

/**
* 扣减账户金额
* @param userId
* @param money
* @return
*/
int decrease(String userId, BigDecimal money);
}

7.在 net.biancheng.net.dao 包下,创建一个名为 AccountDao 的接口,代码如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package net.biancheng.c.dao;

import net.biancheng.c.entity.Storage;

public interface StorageDao {
/**
* 查询商品的库存
* @param productId
* @return
*/
Storage selectByProductId(String productId);

/**
* 扣减商品库存
* @param record
* @return
*/
int decrease(Storage record);
}

8.在 net.biancheng.net.dao 包下,创建一个名为 StorageDao 的接口,代码如下。

1
package net.biancheng.c.dao;import net.biancheng.c.entity.Storage;public interface StorageDao {    /**     * 查询商品的库存     * @param productId     * @return     */    Storage selectByProductId(String productId);    /**     * 扣减商品库存     * @param record     * @return     */    int decrease(Storage record);}

9.在 net.biancheng.c.dao.impl 包下,创建 OrderDao 的实现类 OrderDaoImpl,代码如下。

1
package net.biancheng.c.dao.impl;import net.biancheng.c.dao.OrderDao;import net.biancheng.c.entity.Order;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;@Repositorypublic class OrderDaoImpl implements OrderDao {    @Autowired    private JdbcTemplate jdbcTemplate;    @Override    public int createOrder(Order order) {        String sql = "insert into `order` (order_id,user_id, product_id, `count`, money, status) values (?,?,?,?,?,?)";        int update = jdbcTemplate.update(sql, order.getOrderId(), order.getUserId(), order.getProductId(), order.getCount(), order.getMoney(), order.getStatus());        return update;    }    @Override    public void updateOrderStatus(String orderId, Integer status) {        String sql = " update `order`  set status = 1 where order_id = ? and status = ?;";        jdbcTemplate.update(sql, orderId, status);    }}

\9. 在 net.biancheng.c.dao.impl 包下,创建 AccountDao 的实现类 AccountDaoImpl,代码如下。

1
package net.biancheng.c.dao.impl;import net.biancheng.c.dao.AccountDao;import net.biancheng.c.entity.Account;import net.biancheng.c.entity.Order;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;import java.math.BigDecimal;@Repositorypublic class AccountDaoImpl implements AccountDao {    @Autowired    private JdbcTemplate jdbcTemplate;    @Override    public Account selectByUserId(String userId) {        String sql = "  select * from account where user_id = ?";        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), userId);    }    @Override    public int decrease(String userId, BigDecimal money) {        String sql = "UPDATE account SET residue = residue - ?, used = used + ? WHERE user_id = ?;";        return jdbcTemplate.update(sql, money, money, userId);    }}

\10. 在 net.biancheng.c.dao.impl 包下,创建 StorageDao 的实现类 StorageDaoImpl,代码如下。

1
package net.biancheng.c.dao.impl;import net.biancheng.c.dao.StorageDao;import net.biancheng.c.entity.Storage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.BeanPropertyRowMapper;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.stereotype.Repository;@Repositorypublic class StorageDaoImpl implements StorageDao {    @Autowired    private JdbcTemplate jdbcTemplate;    @Override    public Storage selectByProductId(String productId) {        String sql = "select *   from storage where product_id = ?";        return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Storage>(Storage.class), productId);    }    @Override    public int decrease(Storage record) {        String sql = " update storage set  used =? ,residue=? where product_id=?";        return jdbcTemplate.update(sql, record.getUsed(), record.getResidue(), record.getProductId());    }}
  1. 在 net.biancheng.c.service 包下,创建一个名为 OrderService 的接口,代码如下。
1
package net.biancheng.c.service;import net.biancheng.c.entity.Order;public interface OrderService {    /**     * 创建订单     * @param order     * @return     */    public void createOrder(Order order);}
  1. 在 net.biancheng.c.service.impl 包下,创建 OrderService 的实现类 OrderServiceImpl,代码如下。
1
package net.biancheng.c.service.impl;import net.biancheng.c.dao.AccountDao;import net.biancheng.c.dao.OrderDao;import net.biancheng.c.dao.StorageDao;import net.biancheng.c.entity.Account;import net.biancheng.c.entity.Order;import net.biancheng.c.entity.Storage;import net.biancheng.c.service.OrderService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.text.SimpleDateFormat;import java.util.Date;@Service("orderService")public class OrderServiceImpl implements OrderService {    @Autowired    private OrderDao orderDao;    @Autowired    private AccountDao accountDao;    @Autowired    private StorageDao storageDao;    @Override    public void createOrder(Order order) {        //自动生成订单 id        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS");        String format = df.format(new Date());        String orderId = order.getUserId() + order.getProductId() + format;        System.out.println("自动生成的订单 id 为:" + orderId);        order.setOrderId(orderId);        System.out.println("开始创建订单数据,订单号为:" + orderId);        //创建订单数据        orderDao.createOrder(order);        System.out.println("订单数据创建完成,订单号为:" + orderId);        System.out.println("开始查询商品库存,商品 id 为:" + order.getProductId());        Storage storage = storageDao.selectByProductId(order.getProductId());        if (storage != null && storage.getResidue().intValue() >= order.getCount().intValue()) {            System.out.println("商品库存充足,正在扣减商品库存");            storage.setUsed(storage.getUsed() + order.getCount());            storage.setResidue(storage.getTotal().intValue() - storage.getUsed());            int decrease = storageDao.decrease(storage);            System.out.println("商品库存扣减完成");        } else {            System.out.println("警告:商品库存不足,正在执行回滚操作!");            throw new RuntimeException("库存不足");        }        System.out.println("开始查询用户的账户金额");        Account account = accountDao.selectByUserId(order.getUserId());        if (account != null && account.getResidue().intValue() >= order.getMoney().intValue()) {            System.out.println("账户金额充足,正在扣减账户金额");            accountDao.decrease(order.getUserId(), order.getMoney());            System.out.println("账户金额扣减完成");        } else {            System.out.println("警告:账户余额不足,正在执行回滚操作!");            throw new RuntimeException("账户余额不足");        }        System.out.println("开始修改订单状态,未完成》》》》》已完成");        orderDao.updateOrderStatus(order.getOrderId(), 0);        System.out.println("修改订单状态完成!");    }}
  1. 在 src 目录下,创建一个 jdbc.properties,配置内容如下。
1
2
3
4
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring-tx-db
jdbc.username=root
jdbc.password=root
  1. 在 src 目录下,创建一个 Spring 的 XML 配置文件 Beans.xml,配置内容如下。
1
<?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: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-3.0.xsd        http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context.xsd        http://www.springframework.org/schema/aop        http://www.springframework.org/schema/aop/spring-aop.xsd        http://www.springframework.org/schema/tx        http://www.springframework.org/schema/tx/spring-tx.xsd">    <!--开启组件扫描-->    <context:component-scan base-package="net.biancheng.c"></context:component-scan>    <!--引入 jdbc.properties 中的配置-->    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>    <!--定义数据源 Bean-->    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">        <!--数据库连接地址-->        <property name="url" value="${jdbc.url}"/>        <!--数据库的用户名-->        <property name="username" value="${jdbc.username}"/>        <!--数据库的密码-->        <property name="password" value="${jdbc.password}"/>        <!--数据库驱动-->        <property name="driverClassName" value="${jdbc.driver}"/>    </bean>    <!--定义 JdbcTemplate Bean-->    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">        <!--将数据源的 Bean 注入到 JdbcTemplate 中-->        <property name="dataSource" ref="dataSource"></property>    </bean>    <!--配置事务管理器-->    <bean id="transactionManager"          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="dataSource"></property>    </bean>    <!--配置通知-->    <tx:advice id="tx-advice">        <!--配置事务参数-->        <tx:attributes>            <!--name 指定哪些方法上添加事务-->            <tx:method name="create*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="10"/>        </tx:attributes>    </tx:advice>    <!--配置切点和切面-->    <aop:config>        <!--配置切点-->        <aop:pointcut id="tx-pt" expression="execution(* net.biancheng.c.service.impl.OrderServiceImpl.*(..))"/>        <!--配置切面-->        <aop:advisor advice-ref="tx-advice" pointcut-ref="tx-pt"></aop:advisor>    </aop:config></beans>
  1. 在 net.biancheng.c 包下,创建一个名为 MainApp 的类,代码如下。
1
package net.biancheng.c;import net.biancheng.c.entity.Order;import net.biancheng.c.service.OrderService;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.math.BigDecimal;public class MainApp {    public static void main(String[] args) {        ApplicationContext context2 = new ClassPathXmlApplicationContext("Beans.xml");        OrderService orderService = context2.getBean("orderService", OrderService.class);        Order order = new Order();        //设置商品 id        order.setProductId("1");        //商品数量为 30        order.setCount(30);        //商品金额为 600        order.setMoney(new BigDecimal(600));        //设置用户 id        order.setUserId("1");        //订单状态为未完成        order.setStatus(0);        orderService.createOrder(order);    }}
  1. 执行 MainApp 类中 main 方法,控制台输出如下。
1
2
3
4
5
6
7
8
9
10
11
自动生成的订单 id 为:1120220111173635296
开始创建订单数据,订单号为:1120220111173635296
订单数据创建完成,订单号为:1120220111173635296
开始查询商品库存,商品 id 为:1
商品库存充足,正在扣减商品库存
商品库存扣减完成
开始查询用户的账户金额
账户金额充足,正在扣减账户金额
账户金额扣减完成
开始修改订单状态,未完成》》》》》已完成
修改订单状态完成!
  1. 分别查看订单(order)表、商品库存(storage)表和账户(account)表中的数据,结果如下。
id order_id user_id product_id count money status
1 1120220111173635296 1 1 30 600 1

订单(order)表

id product_id total used residue
1 1 100 30 70

商品库存(storage)表

id user_id total used residue
1 1 1000 600 400

账户(account)表

\18. 再次执行 MainApp 中的 main 方法,控制台输出如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
自动生成的订单 id 为:1120220111175556986
开始创建订单数据,订单号为:1120220111175556986
订单数据创建完成,订单号为:1120220111175556986
开始查询商品库存,商品 id 为:1
商品库存充足,正在扣减商品库存
商品库存扣减完成
开始查询用户的账户金额
警告:账户余额不足,正在执行回滚操作!
Exception in thread "main" java.lang.RuntimeException: 账户余额不足
at net.biancheng.c.service.impl.OrderServiceImpl.createOrder(OrderServiceImpl.java:61)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at com.sun.proxy.$Proxy13.createOrder(Unknown Source)
at net.biancheng.c.MainApp.main(MainApp.java:25)