SpringBoot 事务管理 @Transactional 实现转账功能(MyBatisPlus)

SpringBoot 结合 MyBatisPlus 实现银行转账案例,使用 @Transactional 声明式事务保证原子性,包含实体类、Mapper、Service、Controller 完整代码,解决转账失败数据不一致问题。

数据库表设计

创建 user 表,包含用户ID、姓名、余额字段:

1
2
3
4
5
6
CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `balance` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

实体类 User

 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
import com.baomidou.mybatisplus.annotation.TableName;
import java.math.BigDecimal;

@TableName("user")
public class User {
    private Long id;
    private String name;
    private BigDecimal balance;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public BigDecimal getBalance() {
        return balance;
    }

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

Mapper 层

1
2
3
4
5
6
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

Service 层(事务核心)

 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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 转账方法(添加事务)
     * @param fromUserId 转出用户ID
     * @param toUserId 转入用户ID
     * @param amount 转账金额
     */
    @Transactional
    public void transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
        // 查询转出账户
        User fromUser = userMapper.selectById(fromUserId);
        // 查询转入账户
        User toUser = userMapper.selectById(toUserId);

        // 判断余额是否充足
        if (fromUser.getBalance().compareTo(amount) < 0) {
            throw new RuntimeException("余额不足,转账失败");
        }

        // 扣减转出账户余额
        fromUser.setBalance(fromUser.getBalance().subtract(amount));
        userMapper.updateById(fromUser);

        // 增加转入账户余额
        toUser.setBalance(toUser.getBalance().add(amount));
        userMapper.updateById(toUser);
    }
}

Controller 层

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/transfer")
    public String transfer(
            @RequestParam Long fromUserId,
            @RequestParam Long toUserId,
            @RequestParam BigDecimal amount) {
        userService.transfer(fromUserId, toUserId, amount);
        return "转账成功";
    }
}

关键说明

  • @Transactional 注解保证方法内所有数据库操作要么全部成功,要么全部失败
  • 抛出异常自动回滚,保证数据一致性
  • 适用于转账、支付、订单等需要强一致性的业务场景

依赖(pom.xml)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3.1</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

每日更新学习资料、行业资讯、技术干货与疑难解答,免费分享资源下载,助力学习者高效提升。