作者:技术笔记 | 日期:2026-01-25 | 分类:ORM框架、Java开发、数据库操作

本文专为Java后端开发者打造,聚焦MyBatis-Plus(简称MP)的核心用法与企业级实践,从基础集成到高级特性全覆盖。全文包含20+核心API、10大实操场景、30+代码片段,适配Spring Boot 3.x环境,既适合新手快速入门,也可作为资深开发者的进阶参考手册,建议收藏备用。

一、MyBatis-Plus核心认知

1.1 什么是MyBatis-Plus

MyBatis-Plus是基于MyBatis的增强工具,核心定位是“简化MyBatis开发”,在保留MyBatis原生功能的基础上,提供了CRUD接口自动生成、条件构造器、分页插件、逻辑删除等一系列实用特性,无需编写大量XML映射文件和重复SQL,大幅提升数据库操作效率。

MyBatis-Plus由国内团队苞米豆(baomidou)开发维护,完全兼容MyBatis生态,支持所有MyBatis原生用法,同时提供了更简洁的API和更丰富的功能扩展,已成为国内Java项目中ORM框架的首选方案之一。

1.2 核心优势

  • 零侵入增强:不改变MyBatis原有架构,无需修改现有代码即可集成,平滑过渡;

  • CRUD自动生成:内置BaseMapper接口,提供全套CRUD方法,无需编写XML和SQL;

  • 强大条件构造器:通过Lambda表达式或API构建复杂查询条件,替代繁琐的XML条件拼接;

  • 丰富插件支持:内置分页、逻辑删除、乐观锁、性能分析等插件,开箱即用;

  • 多数据库适配:支持MySQL、Oracle、SQL Server、PostgreSQL等主流数据库,适配灵活;

  • 代码生成器:可自动生成Entity、Mapper、Service、Controller层代码,减少重复编码。

1.3 适用场景

MyBatis-Plus适用于所有使用MyBatis的Java项目,尤其适合以下场景:

  • 快速开发项目:需减少重复SQL编写,提升开发效率;

  • 复杂查询场景:需动态构建查询条件,避免XML中繁琐的条件判断;

  • 微服务项目:轻量化ORM工具,适配微服务架构的简洁性需求;

  • 团队协作项目:统一数据库操作规范,降低代码维护成本;

  • 多数据库适配项目:无需手动修改SQL即可适配不同数据库。

1.4 技术选型对比

对比维度

MyBatis(原生)

MyBatis-Plus

Hibernate

核心定位

轻量级ORM,手动编写SQL

MyBatis增强,自动生成SQL

全自动化ORM,无需编写SQL

SQL控制度

高,完全手动控制

中高,自动生成+手动扩展

低,自动生成,难优化

开发效率

低,需编写大量XML/SQL

高,CRUD自动生成

高,全自动化映射

灵活性

高,适配复杂业务场景

高,兼容MyBatis原生扩展

低,复杂场景适配困难

学习成本

中,需掌握SQL和XML

中低,基于MyBatis,易于上手

高,需掌握HQL和复杂配置

二、MyBatis-Plus集成步骤(核心)

本节以“Spring Boot 3.x + Maven + MySQL 8.x”环境为例,讲解MyBatis-Plus的完整集成流程,其他环境(如Gradle、Oracle)仅需微调依赖和配置。

2.1 环境准备

  • JDK:17+(Spring Boot 3.x强制要求);

  • Spring Boot:3.0.x及以上(本文使用3.3.2稳定版);

  • 数据库:MySQL 8.0+(或其他主流数据库);

  • 构建工具:Maven 3.6+;

  • IDE:IntelliJ IDEA(推荐)。

2.2 引入依赖

在pom.xml中添加MyBatis-Plus核心依赖和数据库驱动依赖,无需额外引入MyBatis依赖(MP已内置):

《!-- MyBatis-Plus核心依赖 --》

《dependency》

《groupId》com.baomidou《/groupId》

《artifactId》mybatis-plus-boot-starter《/artifactId》

《version》3.5.5《/version》 <!-- 稳定版,适配Spring Boot 3.x -->

《/dependency》

《!-- MySQL驱动依赖 --》

《dependency》

《groupId》com.mysql《/groupId》

《artifactId》mysql-connector-j《/artifactId》

《scope》runtime《/scope》

《/dependency》

《!-- 可选: lombok,简化实体类编写 --》

《dependency》

《groupId》org.projectlombok《/groupId》

《artifactId》lombok《/artifactId》

《optional》true《/optional》

《/dependency》

说明:MyBatis-Plus版本需与Spring Boot版本适配,Spring Boot 3.x对应MP 3.5.3+版本,避免版本冲突。

2.3 基础配置

MyBatis-Plus的配置主要分为数据库连接配置和MP专属配置,通过application.yml文件配置,简洁高效。

2.3.1 数据库连接配置

配置MySQL数据库连接信息,适配MySQL 8.x驱动:

spring:

datasource:

driver-class-name: com.mysql.cj.jdbc.Driver # MySQL 8.x驱动类

url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false

username: root # 数据库用户名

password: 123456 # 数据库密码

2.3.2 MyBatis-Plus专属配置

添加MP核心配置,包括Mapper扫描路径、日志打印、主键策略等:

mybatis-plus:

mapper-locations: classpath:mapper/**/*.xml # Mapper XML文件扫描路径(可选)

type-aliases-package: com.example.mpdemo.entity # 实体类别名包路径,简化XML中的类引用

configuration:

map-underscore-to-camel-case: true # 开启下划线转驼峰命名映射(默认开启)

log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志打印方式,控制台输出SQL

global-config:

db-config:

id-type: auto # 主键生成策略:auto为数据库自增,可选ASSIGN_ID(雪花算法)等

table-prefix: mp_ # 数据库表名前缀,实体类无前缀时自动拼接(可选)

2.4 核心目录结构

集成完成后,项目核心目录结构如下,遵循分层开发规范:

com.example.mpdemo

├── entity # 实体类(对应数据库表)

│ └── User.java

├── mapper # Mapper接口(继承BaseMapper)

│ └── UserMapper.java

├── service # 业务层

│ ├── UserService.java # 接口

│ └── impl # 实现类

│ └── UserServiceImpl.java

├── controller # 控制层

│ └── UserController.java

└── MpDemoApplication.java # 启动类

2.5 启动类配置

在Spring Boot启动类上添加@MapperScan注解,指定Mapper接口扫描路径,无需在每个Mapper接口上添加@Mapper注解:

package com.example.mpdemo;

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

@MapperScan("com.example.mpdemo.mapper") // 扫描Mapper接口包路径

public class MpDemoApplication {

public static void main(String[] args) {

SpringApplication.run(MpDemoApplication.class, args);

}

}

三、核心功能使用(重点)

本节讲解MyBatis-Plus的核心功能,包括实体类映射、BaseMapper CRUD、条件构造器、分页插件等,配套完整代码示例,可直接复制落地。

3.1 实体类映射

实体类是数据库表的映射,通过MP注解指定表名、字段名、主键策略等,结合Lombok可大幅简化代码。

3.1.1 常用注解

  • @TableName:指定数据库表名(实体类名与表名不一致时使用);

  • @TableId:指定主键字段,配置主键生成策略;

  • @TableField:指定普通字段,处理字段名不一致、忽略字段等场景;

  • @TableLogic:指定逻辑删除字段。

3.1.2 实体类示例

假设数据库存在表mp_user(对应实体类User),表结构如下:

CREATE TABLE mp_user (

id BIGINT PRIMARY KEY AUTO_INCREMENT, # 主键自增

user_name VARCHAR(20) NOT NULL, # 用户名(下划线命名)

age INT, # 年龄

email VARCHAR(50), # 邮箱

create_time DATETIME DEFAULT NOW(), # 创建时间

is_deleted TINYINT DEFAULT 0 # 逻辑删除字段(0未删,1已删)

);

对应的实体类编写:

package com.example.mpdemo.entity;

import com.baomidou.mybatisplus.annotation.*;

import lombok.Data;

import java.time.LocalDateTime;

@Data // Lombok注解,自动生成getter、setter、toString等方法

// @TableName("mp_user") // 若实体类名与表名一致(忽略前缀)可省略,此处mp_为全局前缀

public class User {

@TableId(type = IdType.AUTO) // 主键自增策略,与全局配置一致可省略

private Long id;

// 字段名不一致(实体类驼峰,数据库下划线),开启全局下划线转驼峰可省略

private String userName;

private Integer age;

private String email;

// 自动填充创建时间,需配合自动填充插件使用

@TableField(fill = FieldFill.INSERT)

private LocalDateTime createTime;

// 逻辑删除字段,0未删,1已删

@TableLogic

private Integer isDeleted;

}

3.2 BaseMapper CRUD操作

MyBatis-Plus提供BaseMapper接口,包含全套CRUD方法,Mapper接口只需继承BaseMapper,无需编写任何方法和XML,即可实现数据库操作。

3.2.1 Mapper接口定义

package com.example.mpdemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import com.example.mpdemo.entity.User;

// 继承BaseMapper,指定实体类泛型

public interface UserMapper extends BaseMapper<User> {

// 无需编写方法,BaseMapper已提供全套CRUD

}

3.2.2 核心CRUD方法示例

在Service或Controller中注入UserMapper,直接调用方法即可,以下为常用方法示例:

package com.example.mpdemo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.example.mpdemo.entity.User;

import com.example.mpdemo.mapper.UserMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController

public class UserController {

@Autowired

private UserMapper userMapper;

// 1. 新增用户

@GetMapping("/user/add")

public String addUser() {

User user = new User();

user.setUserName("张三");

user.setAge(25);

user.setEmail("zhangsan@example.com");

// insert方法返回受影响行数

int rows = userMapper.insert(user);

return rows > 0 ? "新增成功" : "新增失败";

}

// 2. 根据ID查询用户

@GetMapping("/user/{id}")

public User getUserById(@PathVariable Long id) {

// selectById根据主键查询

return userMapper.selectById(id);

}

// 3. 查询所有用户

@GetMapping("/user/list")

public List<User> getUserList() {

// selectList传入null表示无查询条件,查询所有

return userMapper.selectList(null);

}

// 4. 根据ID修改用户

@GetMapping("/user/update/{id}")

public String updateUser(@PathVariable Long id) {

User user = new User();

user.setId(id);

user.setAge(26); // 仅修改年龄字段

int rows = userMapper.updateById(user);

return rows > 0 ? "修改成功" : "修改失败";

}

// 5. 根据ID删除用户(逻辑删除)

@GetMapping("/user/delete/{id}")

public String deleteUser(@PathVariable Long id) {

int rows = userMapper.deleteById(id);

return rows > 0 ? "删除成功" : "删除失败";

}

}

3.3 条件构造器(QueryWrapper/LambdaQueryWrapper)

当需要复杂查询条件(如多条件筛选、排序、模糊查询)时,可使用MP提供的条件构造器,替代XML中的SQL条件拼接,支持Lambda表达式,避免字段名硬编码。

3.3.1 QueryWrapper(普通方式)

通过字段名字符串构建条件,适用于简单场景:

// 多条件查询:年龄大于20且邮箱不为空,按年龄降序排序

@GetMapping("/user/list/condition")

public List<User> getUserByCondition() {

QueryWrapper<User> queryWrapper = new QueryWrapper<>();

// 年龄 > 20

queryWrapper.gt("age", 20);

// 邮箱不为空

queryWrapper.isNotNull("email");

// 按年龄降序排序

queryWrapper.orderByDesc("age");

return userMapper.selectList(queryWrapper);

}

3.3.2 LambdaQueryWrapper(推荐方式)

通过Lambda表达式构建条件,避免字段名硬编码,支持编译期校验:

// 模糊查询:用户名包含"张",年龄在18-30之间,按创建时间升序排序

@GetMapping("/user/list/lambda")

public List<User> getUserByLambda() {

LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();

// 用户名模糊查询(包含"张")

lambdaWrapper.like(User::getUserName, "张");

// 年龄在18-30之间(闭区间)

lambdaWrapper.between(User::getAge, 18, 30);

// 按创建时间升序排序

lambdaWrapper.orderByAsc(User::getCreateTime);

return userMapper.selectList(lambdaWrapper);

}

3.3.3 常用条件方法

方法名

说明

示例

eq

等于(=)

eq("age", 25) → age = 25

ne

不等于(≠)

ne("age", 25) → age ≠ 25

gt

大于(>)

gt("age", 20) → age > 20

ge

大于等于(≥)

ge("age", 20) → age ≥ 20

lt

小于(<)

lt("age", 30) → age < 30

le

小于等于(≤)

le("age", 30) → age ≤ 30

like

模糊查询(%值%)

like("userName", "张") → user_name like '%张%'

between

区间查询(值1到值2)

between("age", 18, 30) → age between 18 and 30

orderByAsc

升序排序

orderByAsc("age") → order by age asc

orderByDesc

降序排序

orderByDesc("age") → order by age desc

3.4 分页插件

MyBatis-Plus内置分页插件,无需编写分页SQL,只需配置插件并使用Page对象即可实现分页查询,支持物理分页(而非内存分页),性能更优。

3.4.1 配置分页插件

创建MyBatis-Plus配置类,注册分页插件:

package com.example.mpdemo.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 {

// 注册分页插件

@Bean

public MybatisPlusInterceptor mybatisPlusInterceptor() {

MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

// 添加分页插件,指定数据库类型(MySQL)

interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

return interceptor;

}

}

3.4.2 分页查询示例

使用Page对象指定页码和每页条数,结合条件构造器实现分页:

// 分页查询:第1页,每页10条,条件为年龄大于20

@GetMapping("/user/page")

public Page<User> getUserPage() {

// Page<T>(当前页码, 每页条数),页码从1开始

Page&lt;User&gt; page = new Page<>(1, 10);

LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();

lambdaWrapper.gt(User::getAge, 20);

// selectPage返回Page对象,包含分页信息和数据列表

Page<User> userPage = userMapper.selectPage(page, lambdaWrapper);

// 分页信息:总条数、总页数、当前页码、每页条数

System.out.println("总条数:" + userPage.getTotal());

System.out.println("总页数:" + userPage.getPages());

return userPage;

}

四、高级特性

本节讲解MyBatis-Plus的高级特性,包括Service层封装、逻辑删除、自动填充、乐观锁等,满足企业级开发的复杂需求。

4.1 Service层封装

MyBatis-Plus提供IService和ServiceImpl接口/类,封装了更丰富的业务层方法(如批量操作、条件查询),比BaseMapper更适合业务层使用,遵循“Service+Mapper”分层规范。

4.1.1 Service接口与实现类

// Service接口,继承IService

package com.example.mpdemo.service;

import com.baomidou.mybatisplus.extension.service.IService;

import com.example.mpdemo.entity.User;

public interface UserService extends IService<User> {

// 可添加自定义业务方法

List<User> getUserByAgeRange(Integer minAge, Integer maxAge);

}

// Service实现类,继承ServiceImpl,注入Mapper

package com.example.mpdemo.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.example.mpdemo.entity.User;

import com.example.mpdemo.mapper.UserMapper;

import com.example.mpdemo.service.UserService;

import org.springframework.stereotype.Service;

import java.util.List;

@Service

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

// 实现自定义方法

@Override

public List<User> getUserByAgeRange(Integer minAge, Integer maxAge) {

LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();

lambdaWrapper.between(User::getAge, minAge, maxAge);

return list(lambdaWrapper); // list方法来自IService

}

}

4.1.2 Service层常用方法

  • saveBatch:批量新增;

  • updateBatchById:批量修改(根据ID);

  • removeByIds:批量删除(根据ID集合);

  • list:查询所有(可结合条件构造器);

  • page:分页查询;

  • count:统计数量(可结合条件构造器)。

4.2 逻辑删除

逻辑删除是指不物理删除数据库记录,而是通过修改标记字段(如is_deleted)表示删除状态,便于数据恢复和历史查询。MyBatis-Plus可自动拦截删除和查询操作,无需手动处理标记字段。

4.2.1 配置逻辑删除

方式1:application.yml全局配置(推荐):

mybatis-plus:

global-config:

db-config:

logic-delete-field: isDeleted # 逻辑删除字段名(实体类字段名)

logic-delete-value: 1 # 删除状态值

logic-not-delete-value: 0 # 未删除状态值

方式2:实体类字段添加@TableLogic注解(优先级高于全局配置):

@TableLogic(value = "0", delval = "1") // value未删值,delval已删值

private Integer isDeleted;

4.2.2 逻辑删除效果

配置完成后,调用deleteById方法时,MP会自动将SQL改为更新操作:

// 原SQL(物理删除):DELETE FROM mp_user WHERE id = ?

// 实际执行SQL(逻辑删除):UPDATE mp_user SET is_deleted = 1 WHERE id = ?

同时,查询操作会自动添加条件is_deleted = 0,过滤已删除数据:

// 原SQL:SELECT * FROM mp_user

// 实际执行SQL:SELECT * FROM mp_user WHERE is_deleted = 0

4.3 自动填充

对于创建时间、修改时间等公共字段,可通过MyBatis-Plus的自动填充功能,在新增/修改时自动赋值,无需手动设置。

4.3.1 配置自动填充处理器

创建自动填充处理器,实现MetaObjectHandler接口:

package com.example.mpdemo.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;

import org.apache.ibatis.reflection.MetaObject;

import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component // 交给Spring管理

public class MyMetaObjectHandler implements MetaObjectHandler {

// 新增时自动填充

@Override

public void insertFill(MetaObject metaObject) {

// 填充创建时间字段(实体类字段名:createTime)

strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());

}

// 修改时自动填充(可选)

@Override

public void updateFill(MetaObject metaObject) {

// 填充修改时间字段(实体类字段名:updateTime)

strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());

}

}

4.3.2 实体类配置填充字段

在实体类字段上添加@TableField(fill = ...)注解,指定填充时机:

@TableField(fill = FieldFill.INSERT) // 仅新增时填充

private LocalDateTime createTime;

@TableField(fill = FieldFill.UPDATE) // 仅修改时填充

private LocalDateTime updateTime;

@TableField(fill = FieldFill.INSERT_UPDATE) // 新增和修改时都填充

private LocalDateTime operateTime;

4.4 乐观锁

乐观锁用于解决并发更新冲突问题,通过版本号字段控制,当更新时检查版本号是否一致,一致则更新并递增版本号,不一致则更新失败。MyBatis-Plus内置乐观锁插件,开箱即用。

4.4.1 配置乐观锁插件

在MyBatis-Plus配置类中添加乐观锁插件:

@Configuration

public class MyBatisPlusConfig {

@Bean

public MybatisPlusInterceptor mybatisPlusInterceptor() {

MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

// 添加分页插件

interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

// 添加乐观锁插件

interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

return interceptor;

}

}

4.4.2 实体类配置版本字段

在实体类中添加版本字段,并添加@Version注解:

@Version // 乐观锁版本号字段

private Integer version; // 版本号,初始值为1

4.4.3 乐观锁使用示例

// 并发更新场景:查询用户后修改年龄,版本号一致则更新成功

@GetMapping("/user/update/optimistic")

public String updateWithOptimisticLock(Long id) {

// 1. 查询用户,获取当前版本号

User user = userService.getById(id);

// 2. 修改字段

user.setAge(user.getAge() + 1);

// 3. 更新操作,MP自动检查版本号

boolean success = userService.updateById(user);

return success ? "更新成功" : "更新失败(并发冲突)";

}

// 实际执行SQL:UPDATE mp_user SET age=?, version=version+1 WHERE id=? AND version=?

五、常见问题与解决方案

5.1 Mapper接口注入失败

  • 原因1:未在启动类添加@MapperScan注解,或扫描路径错误;

  • 原因2:Mapper接口未继承BaseMapper,或泛型指定错误;

  • 解决方案:检查@MapperScan路径,确保Mapper接口继承BaseMapper并指定正确实体类泛型。

5.2 分页插件不生效

  • 原因1:未注册分页插件,或插件配置错误;

  • 原因2:使用selectList而非selectPage方法,未传入Page对象;

  • 解决方案:注册分页插件并指定数据库类型,使用selectPage方法并传入Page对象。

5.3 逻辑删除后仍查询到已删除数据

  • 原因1:未配置逻辑删除字段或状态值;

  • 原因2:实体类字段未添加@TableLogic注解,且无全局配置;

  • 解决方案:配置全局逻辑删除参数,或在实体类字段添加@TableLogic注解。

5.4 自动填充字段未赋值

  • 原因1:未创建自动填充处理器,或处理器未交给Spring管理(缺少@Component);

  • 原因2:实体类字段未添加@TableField(fill = ...)注解,或填充时机指定错误;

  • 解决方案:创建并注册自动填充处理器,为实体类字段配置正确的填充注解。

5.5 乐观锁更新失败

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐