Spring事务管理(SpringBoot)

示例

张三给李四转钱:必须同时执行两个sql

​ 1.查询张三的账户余额,余额充足可以转钱

​ 2.将张三的钱转给李四

李四买一本书

​ 1.查询数据是否还有库存

​ 2.库存充足,将数据加载到购物车

当多条sql语句进行增删改操作时,需要添加事务,因为事务是能保证多条sql语句不出错的关键

事务的四大特征

1.原子性

事务当中包含多条sql语句,这些sql语句是不可再分的,要么同时成功,要么同时失败

2.隔离性

多个事务之间的执行不能相互影响

事物的隔离级别:读未提交、读已提交、可重复读、串行化

3.持久性

事务一旦提交,不可再更改

4.一致性

事务能够回滚为原来的状态

案例

不加事务、无异常

表t_account

sql 复制代码
DROP TABLE IF EXISTS `t_account`;
CREATE TABLE `t_account`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
  `balance` double DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;


INSERT INTO `t_account` VALUES (1, '张三', 1000);
INSERT INTO `t_account` VALUES (2, '淘宝', 0);
INSERT INTO `t_account` VALUES (3, '京东', 0);

实体类Account

java 复制代码
package com.qcby.springbootdemo.model;

public class Account {
    private Integer id;
    private String name;
    private Double balance;

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getBalance() {
        return balance;
    }

    public void setBalance(Double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", balance=" + balance +
                '}';
    }
}

AccountDao接口

java 复制代码
package com.qcby.springbootdemo.dao;

import com.qcby.springbootdemo.model.Account;
import org.springframework.transaction.annotation.Transactional;

public interface AccountDao {

    /**
     * 更新账户余额
     * 无论扣款还是入账,都是更新余额
     * @param account 账户对象
     * @return
     */
    int update(Account  account);

    /**
     * 根据id查询账户
     * 转账都要查询余额
     * @param id
     * @return
     */
    Account findById(Integer id);
}

AccountMapper.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qcby.springbootdemo.dao.AccountDao"> <!--对谁进行操作就写谁-->

    <update id="update" parameterType="account">
        update t_account set balance = #{balance} where id = #{id}
    </update>
    <select id="findById" parameterType="int" resultType="account">
        select * from t_account where id = #{id}
    </select>
</mapper>

AccountService接口

事务是一个简单的业务(业务:真是解决的问题),所以要有service层

java 复制代码
package com.qcby.springbootdemo.service;

public interface AccountService {

    int transferMoney(int fromId, int toId, double money);
}

AccountServiceImpl实现类

java 复制代码
package com.qcby.springbootdemo.service.impl;

import com.qcby.springbootdemo.dao.AccountDao;
import com.qcby.springbootdemo.model.Account;
import com.qcby.springbootdemo.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    /**
     * 转账
     * @param fromId 付款人id
     * @param toId 收款人id
     * @param money
     * @return
     */
    @Override
    public int transferMoney(int fromId, int toId, double money) {
        /**
         * 转账业务步骤:
         * 1. 查询付款人账户余额,判断余额是否充足
         * 2.从转出账户(付款人)扣除转账金额,并更新转出账户余额(转出账户余额-转账金额)
         * 3.查询收款人账户余额
         * 4.向转入账户(收款人)转入转账金额,并更新转入账户余额(转入账户余额+转账金额)
         */

        //1.查询付款人账户余额,判断余额是否充足
        Account fromAccount = accountDao.findById(fromId);
        if (fromAccount.getBalance() < money) {
            System.out.println("余额不足,转账失败");
            return 0;
        }

        //2.从转出账户(付款人)扣除转账金额,并更新转出账户余额(转出账户余额-转账金额)
        fromAccount.setBalance(fromAccount.getBalance() - money);
        accountDao.update(fromAccount);

        //3.查询收款人账户余额
        Account toAccount = accountDao.findById(toId);

        //4.向转入账户(收款人)转入转账金额,并更新转入账户余额(转入账户余额+转账金额)
        toAccount.setBalance(toAccount.getBalance() + money);
        accountDao.update(toAccount);
        return 1;
    }
}

AccountController

java 复制代码
package com.qcby.springbootdemo.controller;

import com.qcby.springbootdemo.model.ResponseResult;
import com.qcby.springbootdemo.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/account")
public class AccountController {

    @Autowired
    private AccountService accountService;

    @RequestMapping("/transfer")
    @ResponseBody
    public ResponseResult transfer(int fromId, int toId, double money) {
        int result = accountService.transferMoney(fromId, toId, money);
        if (result == 1){
            return new ResponseResult(200, "转账成功");
        }else {
            return new ResponseResult(999, "转账失败");
        }
    }
}

运行


不加事务,模拟异常

先把数据库的数据还原,然后在业务层模拟异常

再运行

这里是因为我做了异常处理

张三转过去了,但是淘宝没有收到,所以需要事务

加上事务、模拟异常

回到原始状态,加上事务,只需要加一个@Transactional注解即可

再运行:


钱没转过去,事务回滚了

@Transactional注解

可以设置属性

propagation

用于设置事务的传播行为,指一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行

Spring定义了7种传播行为:

描述
Propagation.REQUIRED 默认传播行为,如果有事务运行,当前方法就在这个事务运行;没有就启动一个新事务,并在自己事务内运行
Propagation.REQUIRES_NEW 当前方法必须启动新事务,并在自己事务内运行;若有事务正在运行,将他挂起
Propagation.SUPPORTS 有事务运行,当前方法就在这个事务内运行;没有就不运行在事务中
Propagation.NOT_SUPPORTED 当前方法不运行在事务中,若有运行的事务,将他挂起
Propagation.MANDATORY 当前方法必须运行在事务内,如果没有正在运行的事务,抛出异常
Propagation.NEVER 当前方法不应该运行在事务内,若有运行的事务,抛出异常
Propagation.NESTED 如果有事务在运行,当前方法应在这个事务的嵌套事务内运行;否则启动一个新事务,并在自己事务内运行

isolation

用于设置事务的隔离级别,默认值为:Isolation.DEFAULT

理论上说,事务之间应彼此完全隔离,以避免并发事务所导致的问题,但这样会对性能产生极大的影响,因为事务必须按顺序运行,所以实际开发中,为了提高性能,事务会以较低的隔离级别运行

相关推荐
天若有情6732 小时前
Spring Boot 前后端联调3大经典案例:从入门到实战(通俗易懂版)
spring boot·后端·状态模式
C***11502 小时前
Spring TransactionTemplate 深入解析与高级用法
java·数据库·spring
BD_Marathon2 小时前
SpringBoot——配置文件格式
java·spring boot·后端
indexsunny2 小时前
互联网大厂Java面试实战:Spring Boot与微服务在电商场景的应用解析
java·spring boot·redis·微服务·kafka·gradle·maven
幽络源小助理2 小时前
SpringBoot+小程序高校素拓分管理系统源码 – 幽络源免费分享
spring boot·后端·小程序
smileNicky2 小时前
Lombok @Data 在 IDEA 中运行报错解决方案
java·ide·intellij-idea
计算机学姐2 小时前
基于SpringBoot的汉服租赁系统【颜色尺码套装+个性化推荐算法+数据可视化统计】
java·vue.js·spring boot·后端·mysql·信息可视化·推荐算法
+VX:Fegn08952 小时前
计算机毕业设计|基于springboot + vue建筑材料管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
qq_5470261792 小时前
Maven 仓库管理
java·maven