一、为什么选择这套技术组合?
作为经历过 SSH 到 Spring Boot 全周期的老开发,我想先告诉你:MyBatis-Plus 是 MyBatis 的「懒人福音」。它在保留 MyBatis 灵活性的同时,封装了 90% 的基础 CRUD 操作,让你专注业务逻辑。搭配 Spring Boot 的自动配置,能在 10 分钟内搭建一个可扩展的数据库操作框架。这篇文章会用「银行账户管理」场景带你实战,全程无废话,直接上生产级代码。
二、项目搭建:从 0 到 1 的标准姿势
1. 初始化项目(以 Spring Initializr 为例)
访问 start.spring.io,选择:
- Group:com.example
- Artifact:springboot-mybatisplus-demo
- Spring Boot:3.2.0(2025 年稳定版)
- Dependencies:勾选 Spring Web、MyBatis-Plus、MySQL Driver、Lombok
2. 核心 pom.xml 配置(避坑指南)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus(注意!不是 MyBatis) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>4.5.2</version> <!-- 2025年最新版本 -->
</dependency>
<!-- MySQL 驱动(8.0+版本注意) -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok(减少样板代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- 注意!MyBatis-Plus 不需要 MyBatis 依赖,避免冲突 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
三、核心配置:3 分钟搞定数据库连接
推荐使用 application.yml(比 properties 更优雅)
在 src/main/resources
下创建 application.yml
:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8+ 驱动
url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai # 时区必配
username: root # 你的数据库用户名
password: 123456 # 你的数据库密码
sql:
init:
schema-locations: classpath:sql/schema.sql # 初始化表结构脚本路径
data-locations: classpath:sql/data.sql # 初始化数据脚本路径
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml # Mapper 映射文件位置(本 demo 暂不使用 XML)
global-config:
db-config:
table-prefix: tb_ # 表名统一前缀(如 tb_account)
id-type: auto # 主键类型(auto 表示数据库自增)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印 SQL 日志(开发环境必开)
map-underscore-to-camel-case: true # 驼峰命名自动映射(如 db_field -> javaField)
四、数据库准备:用标准 SQL 建表(附初始化数据)
在 src/main/resources/sql
目录下创建:
1. schema.sql(表结构)
CREATE DATABASE IF NOT EXISTS test_db CHARACTER SET utf8mb4;
USE test_db;
DROP TABLE IF EXISTS tb_account;
CREATE TABLE tb_account (
id BIGINT AUTO_INCREMENT COMMENT '主键',
account_no VARCHAR(32) NOT NULL COMMENT '账号',
account_name VARCHAR(50) NOT NULL COMMENT '账户名',
balance DECIMAL(10, 2) DEFAULT 0.00 COMMENT '余额',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE KEY uk_account_no (account_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '账户表';
2. data.sql(初始化测试数据)
INSERT INTO tb_account (account_no, account_name, balance)
VALUES ('A001', '张三', 1000.00), ('A002', '李四', 2000.00);
五、代码分层:从 Entity 到 Controller 的完整链路
1. Entity 层(实体类)
package com.example.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("tb_account") // 映射表名(与数据库表名一致,若有前缀需匹配配置)
public class Account {
@TableId(type = IdType.AUTO) // 主键策略(对应数据库自增)
private Long id;
@TableField("account_no") // 字段映射(驼峰自动映射时可省略)
private String accountNo; // 数据库字段为 account_no,Java 用驼峰命名
private String accountName; // 自动映射 account_name
private BigDecimal balance; // 金额用 BigDecimal 避免精度问题
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
}
2. Mapper 层(继承 BaseMapper 即可)
package com.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.entity.Account;
// 继承 BaseMapper 后,自动拥有 CRUD 方法
public interface AccountMapper extends BaseMapper<Account> {
// 若有自定义 SQL 可在此添加,本 demo 暂不扩展
}
3. Service 层(业务逻辑层)
package com.example.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.entity.Account;
import com.example.mapper.AccountMapper;
import org.springframework.stereotype.Service;
// IService<Account> 提供基础 CRUD 接口
public interface AccountService extends IService<Account> {
// 可在此添加自定义方法,如查询余额大于指定值的账户
// List<Account> listByBalanceGreaterThan(BigDecimal balance);
}
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService {
// 直接继承 ServiceImpl 即可使用父类方法,如 save、update、list 等
}
4. Controller 层(接口暴露)
package com.example.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.entity.Account;
import com.example.service.AccountService;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
@RestController
@RequestMapping("/api/accounts")
public class AccountController {
private final AccountService accountService;
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
// 查询所有账户
@GetMapping
public List<Account> list() {
return accountService.list();
}
// 根据 ID 查询单个账户
@GetMapping("/{id}")
public Account getById(@PathVariable Long id) {
return accountService.getById(id);
}
// 新增账户(账号唯一校验)
@PostMapping
public boolean save(@RequestBody Account account) {
// 自定义校验:账号不能重复
LambdaQueryWrapper<Account> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Account::getAccountNo, account.getAccountNo());
if (accountService.count(wrapper) > 0) {
return false; // 账号已存在
}
return accountService.save(account);
}
// 更新账户余额
@PutMapping("/{id}/balance")
public boolean updateBalance(@PathVariable Long id, @RequestParam BigDecimal amount) {
// 使用 Lambda 更新条件
return accountService.update()
.set(Account::getBalance, amount)
.eq(Account::getId, id)
.update();
}
// 删除账户(逻辑删除更佳,此处简化为物理删除)
@DeleteMapping("/{id}")
public boolean delete(@PathVariable Long id) {
return accountService.removeById(id);
}
}
六、启动测试:5 分钟验证 CRUD 功能
1. 启动项目
运行 com.example.SpringbootMybatisplusDemoApplication
主类,看到日志输出:
MyBatis-Plus:
==> Preparing: SELECT id,account_no,account_name,balance,create_time FROM tb_account
==> Parameters:
<== Columns: id, account_no, account_name, balance, create_time
<== Row: 1, A001, 张三, 1000.00, 2025-05-22 10:00:00
<== Row: 2, A002, 李四, 2000.00, 2025-05-22 10:00:00
<== Total: 2
说明 SQL 日志已正常打印,数据库连接成功。
2. 接口测试(以 Postman 为例)
① 查询所有账户
-
URL:
GET http://localhost:8080/api/accounts
-
响应:
[ { "id": 1, "accountNo": "A001", "accountName": "张三", "balance": 1000.00, "createTime": "2025-05-22T10:00:00" }, { "id": 2, "accountNo": "A002", "accountName": "李四", "balance": 2000.00, "createTime": "2025-05-22T10:00:00" }]
② 新增账户
-
URL:
POST http://localhost:8080/api/accounts
-
请求体:
{
"accountNo": "A003",
"accountName": "王五",
"balance": 500.00
}
- 响应:
true
(数据库新增一条记录)
③ 更新余额
- URL:
PUT http://localhost:8080/api/accounts/1/balance?amount=1500.00
- 响应:
true
(查询结果中张三的余额变为 1500.00)
④ 删除账户
- URL:
DELETE http://localhost:8080/api/accounts/2
- 响应:
true
(再次查询时李四的记录已消失)
七、老司机经验谈:这些坑你必须避开
-
依赖冲突:
- 确保 pom 中只有 MyBatis-Plus 依赖,没有 MyBatis 原生依赖(会导致类冲突)。
- 若使用 MyBatis-Plus 3.4+,需确保 Spring Boot 版本 ≥ 2.1。
-
字段映射问题:
- 数据库字段是
user_name
,Java 字段用userName
(驼峰自动映射),无需额外配置。 - 若字段名完全不一致,必须用
@TableField("db_field")
显式声明。
- 数据库字段是
-
主键策略:
- 数据库自增用
IdType.AUTO
,分布式场景用IdType.ID_WORKER
(雪花算法)。 - 避免使用
IdType.ID_WORKER_STR
,可能导致 JSON 长整型精度丢失(前端处理问题)。
- 数据库自增用
-
逻辑删除:
-
在 Entity 中添加
@TableLogic
注解,配合全局配置实现软删除,而非物理删除:@TableLogic // 逻辑删除字段(需数据库存在 is_deleted 字段) private Integer isDeleted;
-
-
性能优化:
- 批量插入用
saveBatch
,批量更新用updateBatchById
,避免循环单条操作。 - 复杂查询用
LambdaQueryWrapper
或自定义 XML,避免过度依赖自动方法。
- 批量插入用
八、下一步学习建议
-
进阶功能:
- 分页查询:使用
Page
对象和IPage
接口,配合mybatis-plus.extension.plugins.pagination.PageInterceptor
插件。 - 自动填充:通过
MetaObjectHandler
实现创建时间、更新时间自动填充。 - 乐观锁:解决并发更新问题,使用
@Version
注解和OptimisticLockerInterceptor
。
- 分页查询:使用
-
最佳实践:
- 统一返回结果:封装
Result<T>
类,处理成功 / 失败响应格式。 - 异常处理:添加全局异常处理器,统一处理数据库唯一约束异常、参数校验异常等。
- 日志记录:在 Service 层添加操作日志,使用
@Slf4j
注解和LoggerFactory
。
- 统一返回结果:封装
-
工具推荐:
- 数据库可视化:DataGrip / Navicat(方便查看表结构和数据)。
- 接口测试:Postman / Apifox(生成测试用例)。
- 代码生成:MyBatis-Plus Generator(一键生成 Entity/Mapper/Service/Controller)。
结语
从 SSH 到 Spring Boot + MyBatis-Plus,我见证了 Java 开发的效率革命。这套组合的核心价值在于:用最少的代码实现最大的功能覆盖,同时保留灵活扩展的空间。对于新手,建议先吃透基础 CRUD,再逐步深入条件构造器、插件机制等高级特性。记住:框架是工具,业务理解和代码设计才是核心竞争力。