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

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

相关推荐
_周游7 分钟前
Java8 API 文档搜索引擎_2.索引模块(实现细节)
java·搜索引擎·intellij-idea
鱼跃鹰飞7 分钟前
大厂面试真题-说说Kafka消息的不重复和不丢失
java·分布式·kafka
A懿轩A8 分钟前
【Maven 构建工具】Maven 依赖管理详解:坐标、传递、作用域与依赖冲突解决(一篇搞懂)
java·linux·maven
2601_949543018 分钟前
Flutter for OpenHarmony垃圾分类指南App实战:资讯详情实现
android·java·flutter
cyforkk2 小时前
12、Java 基础硬核复习:集合框架(数据容器)的核心逻辑与面试考点
java·开发语言·面试
身如柳絮随风扬7 小时前
Java中的CAS机制详解
java·开发语言
风筝在晴天搁浅8 小时前
hot100 78.子集
java·算法
故事和你919 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
Configure-Handler10 小时前
buildroot System configuration
java·服务器·数据库
:Concerto10 小时前
JavaSE 注解
java·开发语言·sprint