SpringBoot事务管理

本文介绍了一个简单的账户金额增减案例,展示了如何使用Spring框架的@Transactional注解来控制事务的提交与回滚,包括正常提交和异常回滚的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、建表语句

DROP TABLE IF EXISTS `account`;
CREATE TABLE `account` (
  `account_id` varchar(30) ,
  `account_name` varchar(30),
  `balance` decimal(20,2),
  PRIMARY KEY (`account_id`)
);

insert into account values ('1','admin','1000.10');

2、以操作账户金额为例,模拟正常操作金额提交事务,以及发生异常回滚事务。
其中控制层代码如下:

package com.sinosoft.demo.controller;

import com.sinosoft.demo.model.Account;
import com.sinosoft.demo.services.AccountService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class AccountController {

    @Resource
    AccountService accountService;

    @GetMapping("/")
    public Account getAccount() {
        //查询账户
        return accountService.getAccount();
    }

    @GetMapping("/add")
    public Object addMoney() {
        try {
            accountService.addMoney();
        } catch (Exception e) {
            return "发生异常了:" + accountService.getAccount();
        }
        return getAccount();
    }
    
}

3、在业务层使用 @Transactional 开启事务,执行数据库操作后抛出异常。具体代码如下:

package com.sinosoft.demo.services;

import com.sinosoft.demo.dao.AccountMapper;
import com.sinosoft.demo.model.Account;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

@Service
public class AccountService {

    @Resource
    AccountMapper accountMapper;

    public Account getAccount() {
        return accountMapper.getAccount();
    }

    @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new RuntimeException("发生异常了..");
    }
}

4、数据库层,通过注解来实现账户数据的查询,具体如下:

package com.sinosoft.demo.dao;

import com.sinosoft.demo.model.Account;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

@Mapper
public interface AccountMapper {

    @Select("select * from account where account_id=1")
    Account getAccount();

    @Update("update account set balance = balance+100 where account_id=1")
    void addMoney();
}

5、Account 实体对象如下:

package com.sinosoft.demo.model;

import java.math.BigDecimal;

public class Account {

    private String accountId;
    private String accountName;
    private BigDecimal balance;

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

    public String getAccountId() {
        return accountId;
    }

    public void setAccountId(String accountId) {
        this.accountId = accountId;
    }

    public String getAccountName() {
        return accountName;
    }

    public void setAccountName(String accountName) {
        this.accountName = accountName;
    }

    public BigDecimal getBalance() {
        return balance;
    }

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

6、测试事务,启动应用,访问 http://localhost:8090 ,可以看到账户数据,如下:
在这里插入图片描述
然后访问 http://localhost:8090/add ,可以看到账户余额并没有增加,如下: 也就是说事务开启成功,数据得到回滚。
在这里插入图片描述

7、容易搞错点

使用事务注解@Transactional 之前,应该先了解它的相关属性,避免在实际项目中踩中各种各样的坑点。
java异常体系结构图

常见坑点1:遇到非检测异常时,事务不开启,也无法回滚。

例如下面这段代码,账户余额依旧增加成功,并没有因为后面遇到检测异常而回滚!!

@Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new  SQLException("发生异常了..");
    }

原因分析:因为Spring的默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。如果想针对非检测异常进行事务回滚,可以在@Transactional 注解里使用rollbackFor 属性明确指定异常。
例如下面这样,就可以正常回滚:

 @Transactional(rollbackFor = Exception.class)
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //然后遇到故障
        throw new SQLException("发生异常了..");
    }

常见坑点2: 在业务层捕捉异常后,发现事务不生效。
这是许多新手都会犯的一个错误,在业务层手工捕捉并处理了异常,你都把异常“吃”掉了,Spring自然不知道这里有错,更不会主动去回滚数据。例如:下面这段代码直接导致增加余额的事务回滚没有生效。

 @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //谨慎:尽量不要在业务层捕捉异常并处理
        try {
            throw new SQLException("发生异常了..");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
不要小瞧了这些细节,往前暴露异常很大程度上很能够帮我们快速定位问题,而不是经常在项目上线后出现问题,却无法刨根知道哪里报错。

推荐做法:在业务层统一抛出异常,然后在控制层统一处理。

 @Transactional
    public void addMoney() throws Exception {
        //先增加余额
        accountMapper.addMoney();
        //推荐:在业务层将异常抛出
        throw new RuntimeException("发生异常了..");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值