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>
|