从七年开发视角带你吃透 Spring Boot 整合 MyBatis-Plus(附全流程实战)

14 阅读6分钟

一、为什么选择这套技术组合?

作为经历过 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 WebMyBatis-PlusMySQL DriverLombok

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(再次查询时李四的记录已消失)

七、老司机经验谈:这些坑你必须避开

  1. 依赖冲突

    • 确保 pom 中只有 MyBatis-Plus 依赖,没有 MyBatis 原生依赖(会导致类冲突)。
    • 若使用 MyBatis-Plus 3.4+,需确保 Spring Boot 版本 ≥ 2.1。
  2. 字段映射问题

    • 数据库字段是 user_name,Java 字段用 userName(驼峰自动映射),无需额外配置。
    • 若字段名完全不一致,必须用 @TableField("db_field") 显式声明。
  3. 主键策略

    • 数据库自增用 IdType.AUTO,分布式场景用 IdType.ID_WORKER(雪花算法)。
    • 避免使用 IdType.ID_WORKER_STR,可能导致 JSON 长整型精度丢失(前端处理问题)。
  4. 逻辑删除

    • 在 Entity 中添加 @TableLogic 注解,配合全局配置实现软删除,而非物理删除:

      @TableLogic // 逻辑删除字段(需数据库存在 is_deleted 字段)
      private Integer isDeleted;
      
  5. 性能优化

    • 批量插入用 saveBatch,批量更新用 updateBatchById,避免循环单条操作。
    • 复杂查询用 LambdaQueryWrapper 或自定义 XML,避免过度依赖自动方法。

八、下一步学习建议

  1. 进阶功能

    • 分页查询:使用 Page 对象和 IPage 接口,配合 mybatis-plus.extension.plugins.pagination.PageInterceptor 插件。
    • 自动填充:通过 MetaObjectHandler 实现创建时间、更新时间自动填充。
    • 乐观锁:解决并发更新问题,使用 @Version 注解和 OptimisticLockerInterceptor
  2. 最佳实践

    • 统一返回结果:封装 Result<T> 类,处理成功 / 失败响应格式。
    • 异常处理:添加全局异常处理器,统一处理数据库唯一约束异常、参数校验异常等。
    • 日志记录:在 Service 层添加操作日志,使用 @Slf4j 注解和 LoggerFactory
  3. 工具推荐

    • 数据库可视化:DataGrip / Navicat(方便查看表结构和数据)。
    • 接口测试:Postman / Apifox(生成测试用例)。
    • 代码生成:MyBatis-Plus Generator(一键生成 Entity/Mapper/Service/Controller)。

结语

从 SSH 到 Spring Boot + MyBatis-Plus,我见证了 Java 开发的效率革命。这套组合的核心价值在于:用最少的代码实现最大的功能覆盖,同时保留灵活扩展的空间。对于新手,建议先吃透基础 CRUD,再逐步深入条件构造器、插件机制等高级特性。记住:框架是工具,业务理解和代码设计才是核心竞争力