Spring基于XML实现事务管理(Java学习笔记)

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

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

创建数据表

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');

导人依赖

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

各个类

entity.Account


package org.example.entity;

import java.math.BigDecimal;

public class Account {
    //自增 id
    private Long id;
    //用户 id
    private String userId;
    //账户总金额
    private BigDecimal total;
    //已用账户金额
    private BigDecimal used;
    //剩余账户金额
    private BigDecimal residue;

    public Long getId() {
        return id;
    }

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

    public String getUserId() {
        return userId;
    }

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

    public BigDecimal getTotal() {
        return total;
    }

    public void setTotal(BigDecimal total) {
        this.total = total;
    }

    public BigDecimal getUsed() {
        return used;
    }

    public void setUsed(BigDecimal used) {
        this.used = used;
    }

    public BigDecimal getResidue() {
        return residue;
    }

    public void setResidue(BigDecimal residue) {
        this.residue = residue;
    }
}

entity.Order

package org.example.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;
    }
}

entity.Storage

package org.example.entity;

public class Storage {
    //自增 id
    private Long id;
    //商品 id
    private String productId;
    //商品库存总数
    private Integer total;
    //已用商品数量
    private Integer used;
    //剩余商品数量
    private Integer residue;

    public Long getId() {
        return id;
    }

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

    public String getProductId() {
        return productId;
    }

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

    public Integer getTotal() {
        return total;
    }

    public void setTotal(Integer total) {
        this.total = total;
    }

    public Integer getUsed() {
        return used;
    }

    public void setUsed(Integer used) {
        this.used = used;
    }

    public Integer getResidue() {
        return residue;
    }

    public void setResidue(Integer residue) {
        this.residue = residue;
    }
}

dao.AccountDao

package org.example.dao;


import org.example.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);
}

dao.OrderDao

package org.example.dao;

import org.example.entity.Order;

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

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

dao.StorageDao

package org.example.dao;

import org.example.entity.Storage;

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

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

dao.impl.AccountDaoImpl

package org.example.dao.impl;

import org.example.dao.AccountDao;
import org.example.entity.Account;
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;

@Repository
public 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);
    }
}

dao.impl.OrderDaoImpl

package org.example.dao.impl;


import org.example.dao.OrderDao;
import org.example.entity.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public 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);
    }
}

dao.impl.StorageImpl

package org.example.dao.impl;


import org.example.dao.StorageDao;
import org.example.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;

@Repository
public 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());
    }
}

service.OrderService

package org.example.service;

import org.example.entity.Order;

public interface OrderService {
    /**
     * 创建订单
     * @param order
     * @return
     */
    public void createOrder(Order order);
}

service.OrderServiceImpl

package org.example.service.impl;

import org.example.dao.AccountDao;
import org.example.dao.OrderDao;
import org.example.dao.StorageDao;
import org.example.entity.Account;
import org.example.entity.Order;
import org.example.entity.Storage;
import org.example.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("修改订单状态完成!");
    }
}

jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/data
jdbc.username=root
jdbc.password=123456

Beans.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: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="org.example"/>
    <!--引入 jdbc.properties 中的配置-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--定义数据源 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"/>
    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </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(* org.example.service.impl.OrderServiceImpl.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="tx-advice" pointcut-ref="tx-pt"/>
    </aop:config>
</beans>
        http://www.springframework.org/schema/tx/spring-tx.xsd">

Main

package org.example;

import org.example.entity.Order;
import org.example.service.OrderService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.math.BigDecimal;

public class Main {
    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);
    }
}

运行结果:

在这里插入图片描述

补充:

事务属性配置

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

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

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

学习原文

学习原文

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值