Spring Boot整合MyBatis-Plus实战:简化CRUD操作的最佳实践

Spring Boot整合MyBatis-Plus实战:简化CRUD操作的最佳实践

    • [一、 前言](#一、 前言)
    • [二、 核心概念与优势](#二、 核心概念与优势)
      • [2.1 什么是 MyBatis-Plus?](#2.1 什么是 MyBatis-Plus?)
      • [2.2 为什么选择它?(与传统 MyBatis 对比)](#2.2 为什么选择它?(与传统 MyBatis 对比))
    • [三、 整合架构与工作原理](#三、 整合架构与工作原理)
      • [3.1 整体架构图](#3.1 整体架构图)
      • [3.2 核心机制解析](#3.2 核心机制解析)
    • [四、 Spring Boot 实战整合步骤](#四、 Spring Boot 实战整合步骤)
      • [4.1 环境准备](#4.1 环境准备)
      • [4.2 引入依赖](#4.2 引入依赖)
      • [4.3 数据源配置](#4.3 数据源配置)
      • [4.4 编写实体类](#4.4 编写实体类)
      • [4.5 创建 Mapper 接口](#4.5 创建 Mapper 接口)
      • [4.6 测试 CRUD 操作](#4.6 测试 CRUD 操作)
    • [五、 高级特性实战:条件构造器](#五、 高级特性实战:条件构造器)
      • [5.1 Wrapper 体系结构](#5.1 Wrapper 体系结构)
      • [5.2 常用条件实战](#5.2 常用条件实战)
      • [5.3 LambdaWrapper:类型安全的最佳实践](#5.3 LambdaWrapper:类型安全的最佳实践)
    • [六、 生产级必备功能](#六、 生产级必备功能)
      • [6.1 分页插件](#6.1 分页插件)
      • [6.2 逻辑删除](#6.2 逻辑删除)
      • [6.3 自动填充](#6.3 自动填充)
    • [七、 最佳实践与总结](#七、 最佳实践与总结)
      • [7.1 实战建议](#7.1 实战建议)
      • [7.2 总结](#7.2 总结)

Spring Boot 整合 MyBatis-Plus

  1. 核心概念与优势 2. 架构与工作原理 3. 实战整合步骤 4. 高级特性实战 5. 性能与生产级配置 无侵入性
    通用CRUD
    代码生成器
    CRUD接口
    Inject SQL注入器
    反射与插件机制
    依赖引入
    数据源配置
    实体类注解
    Mapper接口
    条件构造器
    分页插件
    逻辑删除
    SQL拦截分析
    字段自动填充

一、 前言

在企业级 Java 开发中,持久层框架的选择至关重要。MyBatis 凭借其灵活的 SQL 编写能力和动态 SQL 机制,长期占据主导地位。然而,随着业务的复杂化,单表 CRUD(增删改查)操作占据了大量的开发时间,且存在大量重复代码。

MyBatis-Plus(简称 MP) 正是为了解决这一痛点而生。它并没有改变 MyBatis 的核心,而是在其基础上做了增强,只做增强不做改变。本文将结合实战案例,深入探讨如何在 Spring Boot 中高效整合 MyBatis-Plus,实现 CRUD 操作的极致简化。

二、 核心概念与优势

2.1 什么是 MyBatis-Plus?

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

2.2 为什么选择它?(与传统 MyBatis 对比)

特性 传统 MyBatis MyBatis-Plus
单表 CRUD 需手写 Mapper XML 或注解,代码量大 内置通用 Mapper,零 XML,开箱即用
SQL 构建 手写复杂的 WHERE 条件 链式 QueryWrapper,Java 代码构建 SQL
主键策略 手动配置或编写拦截器 支持多种策略(雪花ID、自增等),配置即用
分页 需手写 count 查询和 limit 拼接 内置分页插件,自动 count 和分页
代码生成 依赖第三方工具(如 Generator) 强大代码生成器,可根据表结构生成 Entity/Mapper/Service/Controller

三、 整合架构与工作原理

在实战之前,理解其工作原理能让我们更好地避坑。

3.1 整体架构图

MyBatis-Plus 内部机制
继承
注入
动态构建 SQL
反射获取实体信息
Controller 层
Service 层
Mapper 层接口
BaseMapper
MyBatis-Plus 核心
SqlSession
数据库 JDBC
Entity 实体类
ISqlInjector

SQL注入器
AbstractSQL

SQL构建器

3.2 核心机制解析

  1. SQL 注入器
    MyBatis-Plus 并没有取代 MyBatis,而是在启动时,通过扫描继承了 BaseMapper 的接口,自动将通用的 CRUD SQL(Select、Insert、Update、Delete)"注入"到 MyBatis 的 MappedStatement 中。
  2. 反射与表映射
    当你调用 insert(user) 时,MP 会通过反射解析 User 类(实体类)的注解(如 @TableName@TableId),将其与数据库表结构进行映射,动态生成 SQL 语句。

四、 Spring Boot 实战整合步骤

我们将构建一个简单的用户管理系统,实现对用户表的 CRUD 操作。

4.1 环境准备

  • JDK 1.8+
  • Maven 3.5+
  • MySQL 5.7+ / 8.0+
  • Spring Boot 2.x / 3.x

4.2 引入依赖

pom.xml 中引入核心依赖。注意:引入 mybatis-plus-boot-starter 后,不要 再引入 mybatis-spring-boot-starter,以免冲突。

xml 复制代码
<dependencies>
    <!-- Spring Boot Web 启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- MyBatis-Plus 启动器 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.5</version> <!-- 请使用最新稳定版 -->
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- Lombok (用于简化实体类 Getter/Setter) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

4.3 数据源配置

application.yml 中配置数据库连接信息。这里建议配置 SQL 日志输出,方便调试。

yaml 复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: password
# MyBatis-Plus 配置
mybatis-plus:
  configuration:
    # 开启驼峰命名转换,如数据库字段 user_name 自动映射给 userName
    map-underscore-to-camel-case: true
    # 日志实现,控制台打印 SQL
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      # 全局主键类型 (AUTO:数据库自增, ASSIGN_ID:雪花算法)
      id-type: auto
      # 逻辑删除全局值(配置后,下面会讲)
      logic-delete-field: deleted
      logic-not-delete-value: 0
      logic-delete-value: 1

4.4 编写实体类

创建数据库表对应的实体类 User

java 复制代码
package com.example.mp.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data // Lombok 生成 Getter/Setter
@TableName("sys_user") // 指定表名,如果类名与表名一致可省略
public class User {
    /**
     * 主键策略:
     * IdType.AUTO -> 数据库自增
     * IdType.ASSIGN_ID -> 雪花算法生成 ID (String 或 Long 类型)
     * IdType.ASSIGN_UUID -> UUID
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    // 如果字段名与列名一致,可不加 @TableField
    private String name;
    private Integer age;
    private String email;
    /**
     * 逻辑删除字段
     * 标记逻辑删除:删除操作变为 UPDATE deleted=1
     * 查询操作自动加 WHERE deleted=0
     */
    @TableLogic
    private Integer deleted;
    /**
     * 自动填充策略:插入时自动填充该字段
     */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

4.5 创建 Mapper 接口

这是 MyBatis-Plus 最神奇的地方。我们不需要编写 XML 文件,甚至不需要写任何方法定义,只需要继承 BaseMapper

java 复制代码
package com.example.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mp.entity.User;
import org.apache.ibatis.annotations.Mapper;
// 指定这是一个 Mapper 接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // BaseMapper 中已经包含了基本的 CRUD 方法
    // 这里可以定义自定义的复杂 SQL 方法
}

4.6 测试 CRUD 操作

编写单元测试,验证基础功能。

java 复制代码
package com.example.mp;
import com.example.mp.entity.User;
import com.example.mp.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SpringBootTest
class MpDemoApplicationTests {
    @Autowired
    private UserMapper userMapper;
    // 1. 插入操作
    @Test
    public void testInsert() {
        User user = new User();
        user.setName("张三");
        user.setAge(25);
        user.setEmail("zhangsan@example.com");
        // ID 会根据策略自动回填
        int result = userMapper.insert(user);
        System.out.println("影响行数: " + result + ", ID: " + user.getId());
    }
    // 2. 根据 ID 更新
    @Test
    public void testUpdateById() {
        User user = new User();
        user.setId(1L);
        user.setName("李四");
        // 注意:更新时如果属性为 null,该字段不会被更新(动态 SQL)
        int result = userMapper.updateById(user);
        System.out.println(result);
    }
    // 3. 根据 ID 查询
    @Test
    public void testSelectById() {
        User user = userMapper.selectById(1L);
        System.out.println(user);
    }
    // 4. 批量查询
    @Test
    public void testSelectBatchIds() {
        List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
        users.forEach(System.out::println);
    }
    // 5. 条件查询
    @Test
    public void testSelectByMap() {
        // Map 中的 key 是数据库列名,不是 Java 属性名
        Map<String, Object> columnMap = new HashMap<>();
        columnMap.put("name", "李四");
        columnMap.put("age", 25);
        List<User> users = userMapper.selectByMap(columnMap);
        users.forEach(System.out::println);
    }
    // 6. 根据 ID 删除
    @Test
    public void testDeleteById() {
        // 如果配置了逻辑删除,这里实际执行的是 UPDATE SET deleted=1
        int result = userMapper.deleteById(1L);
        System.out.println(result);
    }
}

五、 高级特性实战:条件构造器

在实际业务中,查询条件往往是动态且复杂的。MyBatis-Plus 提供了强大的 Wrapper(条件构造器),让我们用 Java 代码构建 SQL,避免 XML 拼接。

5.1 Wrapper 体系结构

Wrapper 条件构造器
QueryWrapper
UpdateWrapper
LambdaQueryWrapper
LambdaUpdateWrapper
用于查询构造条件
用于更新构造条件
支持 Lambda 表达式

防止字段名写错
支持 Lambda 表达式

防止字段名写错

5.2 常用条件实战

java 复制代码
@Test
public void testWrapper() {
    // 1. 创建 QueryWrapper 对象
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    
    // 2. 构建查询条件
    // 名字包含 "张"
    queryWrapper.like("name", "张");
    // 年龄在 20 到 30 之间
    queryWrapper.between("age", 20, 30);
    // 邮箱不为空
    queryWrapper.isNotNull("email");
    
    // 执行查询
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
    
    // 3. 模拟组装 SQL: SELECT id,name,age,email... FROM sys_user 
    // WHERE name LIKE %张% AND age BETWEEN 20 AND 30 AND email IS NOT NULL
}

5.3 LambdaWrapper:类型安全的最佳实践

硬编码字符串 "name" 容易出错,且重构时无法自动修改。推荐使用 LambdaQueryWrapper

java 复制代码
@Test
public void testLambdaWrapper() {
    LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>();
    
    // 使用 :: 的方式引用方法,直接指向 User 类的属性,安全且高效
    lambdaQuery.like(User::getName, "王")
               .lt(User::getAge, 40); // lt = less than (小于)
               
    List<User> users = userMapper.selectList(lambdaQuery);
}

六、 生产级必备功能

6.1 分页插件

物理分页是企业开发标配。MP 的分页插件通过 SQL 拦截实现,支持 MySQL、PostgreSQL、Oracle 等多种数据库。
步骤 1:配置插件

java 复制代码
package com.example.mp.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
    /**
     * 配置 MP 拦截器链
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        
        // 添加分页插件
        // 指定数据库类型,例如 DbType.MYSQL, DbType.ORACLE
        PaginationInnerInterceptor pageInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        
        // 设置请求的页面大于最大页后操作,true 调回首页,false 继续请求
        pageInterceptor.setOverflow(false);
        
        // 单页分页条数限制,默认无限制
        pageInterceptor.setMaxLimit(500L);
        
        interceptor.addInnerInterceptor(pageInterceptor);
        return interceptor;
    }
}

步骤 2:使用分页对象

java 复制代码
@Test
public void testPage() {
    // 第 1 页,每页 2 条
    Page<User> page = new Page<>(1, 2);
    
    // 分页查询结果会自动封装回 page 对象中
    userMapper.selectPage(page, null);
    
    System.out.println("当前页数据: " + page.getRecords());
    System.out.println("总条数: " + page.getTotal());
    System.out.println("总页数: " + page.getPages());
    
    // SQL: SELECT * FROM sys_user LIMIT 0,2
    // 注意:MP 会先执行 SELECT COUNT(*) 查询总数
}

6.2 逻辑删除

业务开发中,我们通常不进行物理删除(DELETE),而是采用逻辑删除(标记为 deleted)。

  • 配置方法见 4.3 节(logic-delete-field 等)和 4.4 节(@TableLogic)。
  • 效果
    • 调用 removeById 时,SQL 变为 UPDATE sys_user SET deleted=1 WHERE id=?
    • 调用 selectList 时,SQL 自动带上 WHERE deleted=0
    • 注意:如果在 XML 中手写了 SQL,需要手动处理逻辑删除字段,插件不会干预自定义 XML 的 SQL。

6.3 自动填充

类似于数据库的 DEFAULT CURRENT_TIMESTAMP,但在 Java 端控制更加灵活,适合处理 create_timeupdate_time 等公共字段。
步骤 1:实现元对象处理器接口

java 复制代码
package com.example.mp.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时自动填充
    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    // 更新时自动填充
    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

七、 最佳实践与总结

7.1 实战建议

  1. 字段命名 :数据库字段尽量使用 user_name 下划线风格,Java 实体使用 userName 驼峰风格,利用 MP 自带的驼峰转换,省去大量 @TableField 注解。
  2. Wrapper 滥用 :虽然 Wrapper 很方便,但如果涉及极其复杂的 SQL(如多表关联、Group By 复杂聚合),建议回退到 MyBatis XML 编写,保持代码可读性。MP 允许两者共存。
  3. 主键选择
    • 分布式系统或需要分库分表时,推荐使用 IdType.ASSIGN_ID (雪花算法)。
    • 单体应用且不涉及数据迁移,使用 IdType.AUTO (数据库自增) 更直观。

7.2 总结

Spring Boot 整合 MyBatis-Plus 极大地提升了后端开发效率,将开发者从繁琐的单表 SQL 中解放出来。通过 BaseMapperConditionWrapper 和分页插件,我们可以在几分钟内构建出一个功能完善的持久层。
技术演进路线:

JDBC -> Hibernate/JPA (全自动,但控制力弱) -> MyBatis (半自动,灵活但繁琐) -> MyBatis-Plus (灵活与效率的完美平衡)

掌握 MyBatis-Plus,不仅是掌握了工具,更是理解了"不重复造轮子"和"约定优于配置"的高级工程哲学。希望本文能助你在实际开发中如虎添翼。

相关推荐
麦兜*2 小时前
Spring Boot 整合 Spring Data JPA 入门:只需注解,告别 SQL
spring boot·后端·sql
benpaodeDD2 小时前
黑马SpringBoot3整合springMVC,mybatis
java·spring boot
岁岁种桃花儿2 小时前
Spring Boot核心注解详解:@ResponseBody深度解析与实战
spring boot·spring·mvc
Overt0p2 小时前
抽奖系统(7)
java·开发语言·spring boot·redis·tomcat·rabbitmq
s***87272 小时前
TCP/IP协议栈深度解析技术文章大纲
hive·spring boot
J_liaty3 小时前
前后端跨域处理全指南:Java后端+Vue前端完整解决方案
java·前端·vue.js·spring boot·后端
jason.zeng@15022073 小时前
基于数据库 + JWT 的 Spring Boot Security 完整示例
数据库·spring boot·oracle
颜淡慕潇3 小时前
深度解读 Spring Boot 3.5.9— 工程视角的稳健演进与价值释放
java·spring boot·后端·spring
码界奇点3 小时前
基于SpringBoot与Shiro的细粒度动态权限管理系统设计与实现
java·spring boot·后端·spring·毕业设计·源代码管理