Java(13):13 Mybatis-Plus -ORM框架 MyBatis 最好的搭档

Java(13):13 Mybatis-Plus -ORM框架 MyBatis 最好的搭档
Prorise1. [基础] 快速入门与环境配置
摘要: 本章的目标是用最快的速度搭建一个可以运行 Mybatis-Plus 的最小化 Spring Boot 3 项目。我们将聚焦于核心的依赖配置、数据源连接以及实体类和 Mapper 接口的基础定义,为后续所有章节的学习提供一个简洁、稳定的开发环境。
1.1. Mybatis-Plus 简介与核心优势
对于一位已经熟练掌握 Spring Boot 和原生 MyBatis 的开发者而言,MyBatis 的优点——完全掌控 SQL 的灵活性——毋庸置疑。但与此同时,其固有的开发痛点也同样突出。
“痛点回顾”: 大量的样板代码(Boilerplate Code)充斥在项目中。即便是一个最基础的单表 CRUD 操作,我们依然需要一步步地完成从 Mapper
接口定义到 XML
文件编写的全过程。这种重复性劳动在项目初期和快速迭代中,会显著拖慢开发效率。
为了更直观地展示原生 MyBatis 与 Mybatis-Plus (下文简称 MP) 在开发流程上的天壤之别,我们可以通过一个简单的“根据ID查询用户”功能进行对比:
- Mapper 层:在
UserMapper
接口中定义1
User selectById(Long id);
- XML 层:在
UserMapper.xml
中手写1
2
3<select id="selectById" resultType="com.demo.User">
SELECT * FROM user WHERE id = #{id}
</select> - Service 层:在
UserServiceImpl
中注入UserMapper
并调用1
User user = userMapper.selectById(id);
- Mapper 层:让
UserMapper
接口继承BaseMapper<User>
,无需额外方法。1
public interface UserMapper extends BaseMapper<User> {}
- XML 层:无需任何 XML 或 SQL。
- Service 层:直接调用继承自
IService
的现成方法。1
User user = userService.getById(id);
通过对比,MP 的核心价值主张显而易见:“只做增强,不做改变”。它完美继承了 MyBatis 的所有功能,并通过内置通用 Mapper
和 Service
,将我们从繁琐、重复的 CRUD 代码中彻底解放出来。这使得我们能更专注于复杂的业务逻辑,也正是我们称之为 MyBatis “最佳搭档” 的根本原因。
1.1.1. [面试题] MP、MyBatis 与 JPA 的技术选型对比
在项目中,当面临持久层框架选型时,你是如何看待 Mybatis-Plus、MyBatis 和 JPA (如 Hibernate) 这三者的?它们的优缺点和适用场景分别是什么?
好的面试官。这三者是 Java 持久化领域的代表,我的理解如下:
JPA以Hibernate为代表,是一个全自动ORM框架。它的优点是自动化程度高、开发效率快,缺点是SQL黑盒、难以优化,因此最适用于业务简单的中后台系统。
MyBatis是一个半自动SQL映射框架。它的优点是对SQL有绝对控制权、便于性能优化,缺点是样板代码多、开发效率较低,因此非常适用于SQL逻辑复杂、性能要求高的互联网应用。
Mybatis-Plus是MyBatis的增强工具。它的优点是结合了JPA的便利和MyBatis的灵活,既能快速开发也能精细优化,缺点是学习曲线稍高,因此它适用于绝大多数需要兼顾开发效率和性能的现代Java项目。
总结得很好。
1.2. 项目环境搭建
为了让学习过程聚焦于 Mybatis-Plus 本身,我们将采用最简洁的单模块 Spring Boot 项目结构。
1.2.1. 技术栈版本说明
本教程将基于 2025 年的主流稳定技术栈进行构建,具体版本如下:
技术栈 | 版本 | 说明 |
---|---|---|
JDK | 21 | Long-Term Support (LTS) 长期支持版 |
Spring Boot | 3.4.x | 现代 Java 应用开发的事实标准 |
Mybatis-Plus | 3.5.7+ | 适配 Spring Boot 3 的最新稳定版 |
MySQL Driver | 8.0.33 | 官方推荐的 MySQL 8+ 驱动 |
Maven | 3.8+ | 项目构建与依赖管理工具 |
1.2.2. Maven 依赖配置 (pom.xml
)
首先,创建一个标准的 Spring Boot Maven 项目,并在 pom.xml
中配置好我们的核心依赖。
文件路径: pom.xml
1 |
|
1.2.3. 数据源与 MP 基础配置 (application.yml
)
我们推荐使用 .yml
格式进行配置,因为它层级清晰,更具可读性。请在 src/main/resources/
目录下创建 application.yml
文件。
文件路径: src/main/resources/application.yml
1 | # 服务器端口配置 |
请注意: 在 application.yml
中,spring.datasource.password
字段需要替换为您自己本地 MySQL 数据库的真实密码。
1.3. 核心文件创建
项目的基础框架和配置已经就绪。现在,我们需要创建与数据库交互的核心文件,包括数据表结构、实体类(Entity)、数据访问接口(Mapper)以及配置启动类。
1.3.1. 数据库表结构 (tb_user
)
请在您的 MySQL 数据库中执行以下 SQL 脚本。这份脚本将创建我们项目所需的数据库和 tb_user
表。
设计说明:此表结构严格遵循了《阿里巴巴 Java 开发手册》的规约。我们预先定义了乐观锁 (version
)、逻辑删除 (is_deleted
) 以及审计字段 (gmt_create
, gmt_modified
),这体现了企业级表结构设计的专业性与前瞻性。我们将在后续章节中详细讲解这些字段的应用。
1 | -- 创建数据库(如果不存在) |
1.3.2. 实体类 (UserDO.java
)
实体类是数据库表在 Java 世界中的映射。按照规约,与数据库表直接对应的对象我们称之为 DO (Data Object)。
文件路径: src/main/java/com/example/mpstudy/domain/UserDO.java
1 | package com.example.mpstudy.domain; |
1.3.3. Mapper 接口 (UserMapper.java
)
Mapper 接口是数据访问层(DAO)的核心,它充当了 Java 代码与数据库 SQL 之间的桥梁。
文件路径: src/main/java/com/example/mpstudy/mapper/UserMapper.java
1 | package com.example.mpstudy.mapper; |
1.3.4. 启动类配置 (@MapperScan
)
最后一步,我们需要告诉 Spring Boot 在哪里可以找到我们刚刚创建的 Mapper 接口,以便为它们创建代理实现并纳入 IoC 容器管理。
文件路径: src/main/java/com/example/mpstudy/MpStudyApplication.java
1 | package com.example.mpstudy; |
@MapperScan("com.example.mpstudy.mapper")
: 这个注解的作用是扫描指定的包(com.example.mpstudy.mapper
),并将其中所有被 Mybatis 识别为 Mapper 的接口(通常是继承了 BaseMapper
的接口)自动注册为 Spring Bean。这样,我们就可以在 Service 层或其他地方通过 @Autowired
直接注入并使用它们了。
至此,我们的项目已完全准备就绪,所有基础配置和核心文件均已创建完毕。在下一章,我们将正式开始体验 Mybatis-Plus 强大而便捷的 CRUD 功能。
2. [核心] 通用 CRUD 与 Service 接口
摘要: 本章将讲解 Mybatis-Plus 效率革命的核心:通用 CRUD 功能。我们将学习如何通过继承 BaseMapper
和 ServiceImpl
接口,在不写一行 SQL 的情况下,实现单表的增、删、改、查及批量操作。
2.1. BaseMapper
内置方法详解
BaseMapper
是 Mybatis-Plus 实现通用 CRUD 的基石。我们在上一章让 UserMapper
接口继承了 BaseMapper<UserDO>
,这使得 UserMapper
立刻拥有了十几个功能强大的、无需任何SQL编写的数据库操作方法。
方法名称 | 描述 | 需要传入的参数 |
---|---|---|
insert(T entity) | 插入一条数据 | entity :需要插入的实体类对象 |
deleteById(Serializable id) | 根据 ID 删除数据 | id :要删除的记录的主键 ID |
updateById(T entity) | 根据 ID 更新数据 | entity :包含更新字段的实体类对象 |
selectById(Serializable id) | 根据 ID 查询数据 | id :要查询的记录的主键 ID |
selectList(Wrapper<T> query) | 根据条件查询数据 | query :查询条件,通常使用 QueryWrapper |
delete(Wrapper<T> query) | 根据条件删除数据 | query :删除条件,通常使用 QueryWrapper |
update(Wrapper<T> updateWrapper) | 根据条件更新数据 | updateWrapper :更新条件,通常使用 UpdateWrapper |
selectCount(Wrapper<T> query) | 根据条件统计数据 | query :查询条件,通常使用 QueryWrapper |
selectOne(Wrapper<T> query) | 根据条件查询一条数据 | query :查询条件,通常使用 QueryWrapper |
T
:表示一个实体类对象类型。例如,User
、Order
等。Serializable
:表示可以序列化的类型,通常是主键 ID。Wrapper<T>
:这是 MyBatis-Plus 提供的一个条件构造器类,常用的有QueryWrapper
(用于查询条件)和UpdateWrapper
(用于更新条件)。通过Wrapper
,你可以构建更加复杂的查询或更新条件,我们后续会详细讲解这个
为了验证这些方法的实际效果,我们将通过单元测试来进行演示。
首先,在 src/test/java/com/example/mpstudy/mapper/
目录下,创建一个测试类 UserMapperTest
。
文件路径: src/test/java/com/example/mpstudy/mapper/UserMapperTest.java
1 | package com.example.mpstudy.mapper; |
2.1.1. 插入 (insert
)
insert
方法用于向数据库中插入一条新的记录。
1 | // UserMapperTest.java |
1
2
3
4
5
6
7
8
9
10
11
12
----- 开始执行 insert 测试 -----
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@68a426c3] was not registered for synchronization because synchronization is not active
2025-08-22T09:22:23.605+08:00 INFO 9020 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2025-08-22T09:22:23.805+08:00 INFO 9020 --- [ main] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Added connection com.mysql.cj.jdbc.ConnectionImpl@51141f64
2025-08-22T09:22:23.809+08:00 INFO 9020 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1010480754 wrapping com.mysql.cj.jdbc.ConnectionImpl@51141f64] will not be managed by Spring
==> Preparing: INSERT INTO tb_user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1958701250124349442(Long), Prorise(String), 18(Integer), prorise@163.com(String)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@68a426c3]
受影响的行数: 1
2.1.2. 删除 (deleteById
, deleteByMap
, deleteBatchIds
)
Mybatis-Plus 提供了多种删除数据的方式。
1 |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- ==> Preparing: DELETE FROM tb_user WHERE id=?
-- ==> Parameters: 1826451631520124929(Long)
-- <== Updates: 1
deleteById 受影响行数: 1
-- ==> Preparing: DELETE FROM tb_user WHERE id IN ( ? , ? )
-- ==> Parameters: 1(Long), 2(Long)
-- <== Updates: 2
deleteBatchIds 受影响行数: 2
-- ==> Preparing: DELETE FROM tb_user WHERE name = ? AND age = ?
-- ==> Parameters: Tom(String), 28(Integer)
-- <== Updates: 1
deleteByMap 受影响行数: 1
由于我们测试删除了部分数据,建议再重新向我们的数据库插入新数据回来
2.1.3. 修改 (updateById
)
updateById
会根据传入实体的ID去更新记录。
重要: updateById
方法默认会更新实体中所有字段,即使字段值为 null
。这意味着如果您只想更新某个字段,需要先查询出完整记录,修改后再更新,否则其他字段可能被 null
覆盖。后续章节会讲解如何实现“部分更新”。
1 | // UserMapperTest.java |
1
2
3
4
-- ==> Preparing: UPDATE tb_user SET name=?, age=?, email=?, version=?, is_deleted=?, gmt_create=?, gmt_modified=? WHERE id=?
-- ==> Parameters: null, 22(Integer), null, null, null, null, null, 4(Long)
-- <== Updates: 1
updateById 受影响行数: 1
2.1.4. 查询 (selectById
, selectList
, selectBatchIds
, selectByMap
)
查询是最高频的操作,BaseMapper
同样提供了丰富的查询方法。
1 | // UserMapperTest.java |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- ==> Preparing: SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user WHERE id=?
-- ==> Parameters: 5(Long)
-- <== Total: 1
selectById 查询结果: UserDO(id=5, name=Billie, age=24, email=test5@baomidou.com, version=1, isDeleted=0, gmtCreate=..., gmtModified=...)
-- ==> Preparing: SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user
-- ==> Parameters:
-- <== Total: 5
selectList 查询到的总数: 5
... (打印所有用户)
-- ==> Preparing: SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user WHERE id IN ( ? , ? )
-- ==> Parameters: 4(Long), 5(Long)
-- <== Total: 2
selectBatchIds 查询到的结果:
UserDO(id=4, name=Sandy, age=21, email=test4@baomidou.com, ...)
UserDO(id=5, name=Billie, age=24, email=test5@baomidou.com, ...)
-- ==> Preparing: SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user WHERE name = ?
-- ==> Parameters: Sandy(String)
-- <== Total: 1
selectByMap 查询到的结果:
UserDO(id=4, name=Sandy, age=21, email=test4@baomidou.com, ...)
2.2. IService
和 ServiceImpl
的应用
直接在业务逻辑中注入 Mapper
进行数据库操作是可行的,但这是一种不良实践。专业的开发模式要求在 Controller
(或业务逻辑) 与 Mapper
(数据访问) 之间设立一个 Service 层。
Service 层的职责:
- 封装业务逻辑:处理复杂的业务规则。
- 事务管理:确保多个数据库操作的原子性。
- 解耦:隔离上层应用与底层数据访问的细节。
Mybatis-Plus 同样为 Service 层提供了强大的代码简化方案:IService
接口和 ServiceImpl
实现类。
2.2.1. 业务层接口 (UserService
) 继承 IService
我们首先定义 UserService
接口,它继承 IService<UserDO>
。
文件路径: src/main/java/com/example/mpstudy/service/UserService.java
1 | package com.example.mpstudy.service; |
2.2.2. 业务类实现 (UserServiceImpl
) 继承 ServiceImpl
接著,我們創建 UserService
的實現類,它需要繼承 ServiceImpl
。
文件路径: src/main/java/com/example/mpstudy/service/impl/UserServiceImpl.java
1 | package com.example.mpstudy.service.impl; |
2.2.3. 常用 Service 方法 (save
, remove
, update
, get
, list
)
IService
提供了比 BaseMapper
更符合业务语义的方法名,如 save
对应 insert
,getById
对应 selectById
,list
对应 selectList
。
方法名称 | 描述 | 需要传入的参数 |
---|---|---|
save(T entity) | 插入单条数据 | entity :需要插入的实体类对象 |
saveBatch(Collection<T> list) | 批量插入数据 | list :需要插入的实体类对象集合 |
removeById(Serializable id) | 根据 ID 删除数据 | id :要删除的记录的主键 ID |
remove(Wrapper<T> query) | 根据条件删除数据 | query :删除条件,通常使用 QueryWrapper |
updateById(T entity) | 根据 ID 更新数据 | entity :包含更新字段的实体类对象 |
update(Wrapper<T> updateWrapper) | 根据条件更新数据 | updateWrapper :更新条件,通常使用 UpdateWrapper |
list() | 查询所有数据 | 无(无需参数,查询所有记录) |
list(Wrapper<T> query) | 根据条件查询数据 | query :查询条件,通常使用 QueryWrapper |
getById(Serializable id) | 根据 ID 查询数据 | id :要查询的记录的主键 ID |
count() | 查询数据条数 | 无(返回数据库中记录的总数) |
count(Wrapper<T> query) | 根据条件查询数据条数 | query :查询条件,通常使用 QueryWrapper |
getOne(Wrapper<T> query) | 根据条件查询一条数据 | query :查询条件,通常使用 QueryWrapper |
T
:表示一个实体类对象类型。例如,User
、Order
等。Serializable
:表示可以序列化的类型,通常是主键 ID。Wrapper<T>
:这是 MyBatis-Plus 提供的一个条件构造器类,常用的有QueryWrapper
(用于查询条件)和UpdateWrapper
(用于更新条件)。通过Wrapper
,你可以构建更加复杂的查询或更新条件,我们后续会详细讲解
为了测试 Service 层的功能,我们创建一个新的测试类 UserServiceTest
。
文件路径: src/test/java/com/example/mpstudy/service/UserServiceTest.java
1 | package com.example.mpstudy.service; |
1
2
3
4
5
6
7
8
9
-- ==> Preparing: SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user WHERE id=?
-- ==> Parameters: 5(Long)
-- <== Total: 1
getById 查询结果: UserDO(id=5, name=Billie, age=24, email=test5@baomidou.com, ...)
-- ==> Preparing: SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user
-- ==> Parameters:
-- <== Total: 5
list 查询到的总数: 5
2.2.4. 批量操作 (saveBatch
, updateBatchById
)
IService
也提供了高效的批量操作方法,它会在底层优化 SQL 的执行(例如,通过 Batch
模式),远比我们自己循环调用 save
或 update
要高效。
1 |
|
1
2
3
4
-- ==> Preparing: INSERT INTO tb_user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
-- ==> Parameters: 1826543997233205249(Long), BatchUser1(String), 25(Integer), b1@example.com(String)
-- ==> Parameters: 1826543997233205250(Long), BatchUser2(String), 26(Integer), b2@example.com(String)
批量新增是否成功: true
2.2.5. saveOrUpdate
方法详解
saveOrUpdate
是一个非常智能的方法,它可以根据实体对象的主键(ID)是否存在来自动判断是执行插入还是更新操作。
- 如果实体对象的 ID 为
null
,则执行insert
。 - 如果实体对象的 ID 不为
null
,则执行updateById
。
1 | // UserServiceTest.java |
1
2
3
4
5
6
7
8
9
-- ==> Preparing: INSERT INTO tb_user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
-- ==> Parameters: 1826545199839973378(Long), NewOrUpdateUser(String), 40(Integer), nou@example.com(String)
-- <== Updates: 1
插入操作是否成功: true, 用户ID: 1826545199839973378
-- ==> Preparing: UPDATE tb_user SET name=?, age=?, email=?, version=?, is_deleted=?, gmt_create=?, gmt_modified=? WHERE id=?
-- ==> Parameters: null, 41(Integer), null, null, null, null, null, 1826545199839973378(Long)
-- <== Updates: 1
更新操作是否成功: true
2.3. 自定义接口方法
尽管 Mybatis-Plus 提供的通用 BaseMapper
和 IService
已经能覆盖绝大多数单表操作,但在复杂的业务场景中,我们仍然需要编写自定义的 SQL,例如多表 JOIN
查询、复杂的统计或调用数据库函数等。
Mybatis-Plus 的一个核心优势在于它**“只做增强,不做改变”**。这意味着,我们可以无缝地回归到原生 MyBatis 的开发模式,定义自己的 Mapper 方法,并通过 XML 文件或注解来编写对应的 SQL 语句。
2.3.1. [实践] 自定义 Mapper 接口方法
接下来,我们将为 UserMapper
添加一个自定义方法 selectByName
,用于根据姓名查询用户信息,并为其编写对应的 XML 实现。
第一步:在 UserMapper
接口中定义抽象方法
文件路径: src/main/java/com/example/mpstudy/mapper/UserMapper.java
1 | package com.example.mpstudy.mapper; |
最佳实践: 当 Mapper 方法有多个参数时,强烈建议使用 MyBatis 提供的 @Param("...")
注解为每个参数命名。这能让 XML 文件中的 SQL 通过名称(如 #{name}
)清晰地引用到参数,避免因参数顺序问题导致的错误。
第二步:创建 Mapper XML 映射文件
我们需要在 resources
目录下创建一个与 UserMapper
接口对应的 XML 文件来存放我们的 SQL 语句。
文件路径: src/main/resources/mapper/UserMapper.xml
1 |
|
检查配置: 请确保您的 application.yml
文件中配置了 mybatis-plus.mapper-locations
属性,以便 Mybatis-Plus 能够找到您编写的 XML 文件。mybatis-plus.mapper-locations: classpath*:/mapper/**/*.xml
第三步:编写单元测试进行验证
现在,我们可以在 UserMapperTest
中调用这个新的自定义方法。
文件路径: src/test/java/com/example/mpstudy/mapper/UserMapperTest.java
1 | // UserMapperTest.java (添加新的测试方法) |
1
2
3
4
5
6
-- ==> Preparing: SELECT * FROM tb_user WHERE name = ?
-- ==> Parameters: Tom(String)
-- <== Total: 1
----- 开始执行自定义方法测试 -----
查询到的 Tom 的信息: UserDO(id=3, name=Tom, age=28, email=test3@baomidou.com, version=1, isDeleted=0, gmtCreate=..., gmtModified=...)
----- 自定义方法测试执行完毕 -----
通过以上步骤,我们成功地在 Mybatis-Plus 的体系中集成了自定义的 SQL 查询,这证明了 MP 的高度灵活性和兼容性。对于任何通用方法无法满足的复杂需求,您都可以放心地使用这种方式来解决。
3. [进阶] 从实体映射到复杂关联查询
摘要: 本章是 Mybatis-Plus 从入门到精通的关键。我们将首先掌握如何通过注解精准控制实体与表的映射关系;随后,深入学习 MP 的灵魂——条件构造器(Wrapper),用纯 Java 代码构建任意复杂的单表动态查询;最后,我们将回归 MyBatis 的 XML 精髓,解决 Wrapper 难以处理的多表 JOIN
及一对多、多对多等复杂关联查询场景。
3.1. 实体与表映射注解
在深入学习查询之前,我们必须先打好地基——确保我们的 Java 实体类能够精准地与数据库表结构对应起来。虽然 Mybatis-Plus 提供了强大的自动映射能力,但在实际项目中,类名与表名、属性名与字段名不一致的情况非常普遍。本节将深入讲解如何通过注解来解决这些映射问题。
3.1.1. 自动映射规则回顾
Mybatis-Plus 默认遵循“驼峰与下划线”的自动映射规则,这得益于其内置的 map-underscore-to-camel-case
配置默认为 true
。
- 表名映射: 实体类名
UserDO
会被自动映射为表名user_do
。 - 字段映射: 属性名
gmtCreate
会被自动映射为字段名gmt_create
。
正是因为有此规则,在前面的章节中,我们的 UserDO
即使不加任何注解也能正常工作(如果我们把表名和字段名都改成下划线格式)。但当默认规则不满足需求时,就需要手动配置了。
3.1.2. 表映射: @TableName
当实体类名与表名的映射不符合默认规则时(例如,类名为 UserDO
,而表名为 tb_user
),就需要使用 @TableName
注解来手动指定。
文件路径: src/main/java/com/example/mpstudy/domain/UserDO.java
1 | package com.example.mpstudy.domain; |
3.1.3. 字段映射: @TableField
@TableField
是一个功能强大的注解,用于处理实体属性与表字段之间的各种映射问题。
1. 字段名不匹配
当属性名和字段名的映射关系不符合默认规则时(例如,属性为 email
,但数据库字段为 user_email
),可以使用其 value
属性来指定。
1 | // UserDO.java |
2. 属性在表中不存在 (非表字段)
有时我们希望在实体类中定义一些不与数据库表字段对应的属性,例如用于临时计算或前端展示。可以使用 exist = false
来标记,告诉 MP 忽略这个属性。
1 | // UserDO.java |
1
2
3
4
5
-- 不加 @TableField(exist = false) 时,查询SQL会因试图查询不存在的列而报错:
-- SELECT id, name, ..., user_role FROM tb_user
-- 添加 @TableField(exist = false) 后,生成的查询SQL会自动忽略该字段:
-- SELECT id, name, ... FROM tb_user
3. 控制字段是否参与查询
对于一些敏感信息(如密码)或大字段(如文章内容),我们可能希望在常规列表查询中默认不返回它们,以提高性能和安全性。可以使用 select = false
来实现。
1 | // UserDO.java |
1
2
3
4
5
-- userMapper.selectList(null) 生成的SQL将不包含 password 字段:
-- SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified FROM tb_user
-- 但 userMapper.selectById(1L) 仍然会查询所有字段,包括 password:
-- SELECT id,name,age,email,version,is_deleted,gmt_create,gmt_modified,password FROM tb_user WHERE id=?
3.1.4. 主键映射: @TableId
与主键生成策略 (IdType
)
@TableId
注解专门用于标识实体类中的主键属性。MP 默认会将名为 id
的属性视为主键,但显式使用 @TableId
是更规范的做法。它最重要的功能是可以通过 type
属性指定主键的生成策略。
IdType 枚举值 | 描述 | 适用场景 |
---|---|---|
AUTO | 数据库ID自增。将主键生成交由数据库的自增列处理。 | 本项目选用,兼容性好,便于测试。 |
ASSIGN_ID | 雪花算法。MP 默认策略,生成一个全局唯一的 Long 类型ID。 | 分布式系统,需要全局唯一ID的场景。 |
INPUT | 用户手动输入。ID需要由开发者在插入前手动设置。 | 业务主键明确,或由其他服务生成ID的场景。 |
ASSIGN_UUID | UUID。生成一个随机的32位字符串ID,无序。 | 主键为 String 类型,需要唯一性的场景。 |
NONE | 无策略。未设置主键类型,会跟随全局配置。 | 不推荐单独使用。 |
在我们的 UserDO
中,已经根据您的要求配置为了 IdType.AUTO
。
文件路径: src/main/java/com/example/mpstudy/domain/UserDO.java
1 | // UserDO.java |