3、核心注解:@TableName、@TableId、@TableField、@TableLogic详解
好,咱们直接进入正题。MyBatis-Plus 之所以能让你少写那么多代码,核心秘密就在这几个注解上。说白了,它们就是 MP 和数据库表之间的「翻译官」。你想想看,没有这些注解,MP 怎么知道你的 User 类对应哪张表?哪个字段是主键?
我个人习惯,拿到一个新项目,第一件事就是先把实体类的注解配好。这一步做好了,后面写代码就跟开了挂一样顺畅。
3.1 @TableName:实体类与表的映射
这个注解最简单,也最常用。它的作用就是告诉 MP:我这个 Java 类,对应数据库里的哪张表。
核心作用:建立实体类与数据库表的映射关系。
@TableName("t_user")
public class User {
private Long id;
private String name;
// ...
}
嗯,这里要注意几个细节。如果你不写 @TableName,MP 会默认用类名作为表名。比如 User 类就对应 user 表。但实际项目中,表名往往有前缀,比如 t_user、sys_user 之类的。这时候就必须显式指定。
我的小技巧:如果你们团队的表命名规范统一,比如都是 t_ 开头,可以在全局配置里设置 tablePrefix,这样就不用每个实体类都写 @TableName 了。
// 全局配置
mybatis-plus:
global-config:
db-config:
table-prefix: t_
我在项目中遇到过一个问题:有个同事没加 @TableName,结果 MP 把 User 类映射到了 user 表,但实际表名是 sys_user。查了半天才发现是注解漏了。所以,我建议你养成习惯,每个实体类都显式加上 @TableName,哪怕表名和类名一样。
3.2 @TableId:主键的指定
主键是每张表的灵魂。@TableId 就是用来告诉 MP,哪个字段是主键,以及主键的生成策略是什么。
@TableName("t_user")
public class User {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String name;
// ...
}
这里有两个属性需要你关注:
| 属性 | 说明 | 常用值 |
|---|---|---|
| value | 指定数据库表中的主键字段名 | "id"、"user_id" 等 |
| type | 主键生成策略 | IdType.AUTO、IdType.ASSIGN_ID 等 |
关于主键策略,我重点说几个常用的:
- IdType.AUTO:数据库自增。适合 MySQL 的 auto_increment,简单粗暴。
- IdType.ASSIGN_ID:使用雪花算法生成 ID。适合分布式场景,我现在的项目都在用这个。
- IdType.INPUT:自己输入。适合有业务含义的主键,比如订单号。
避坑指南:我曾经在一个老项目里看到有人用 IdType.NONE,结果插入数据时主键一直为空,报错报得莫名其妙。后来才发现,NONE 表示「不设置」,需要你自己处理。所以,千万别偷懒,一定要显式指定策略。
另外,如果你的实体类里主键字段名和数据库字段名一致,比如都是 id,那 value 属性可以省略。但我不建议这么做,显式写出来更清晰,也方便后来人维护。
3.3 @TableField:非主键字段的映射
这个注解最灵活,也最容易出问题。它用来处理那些「不按套路出牌」的字段映射。
@TableName("t_user")
public class User {
@TableId
private Long id;
@TableField("user_name")
private String name;
@TableField(select = false)
private String password;
@TableField(exist = false)
private String remark;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
}
你看,@TableField 能干的事情很多:
| 属性 | 说明 | 使用场景 |
|---|---|---|
| value | 指定数据库字段名 | Java 字段名和数据库字段名不一致时 |
| exist | 是否为数据库字段 | 实体类中有额外属性,但数据库没有对应字段 |
| select | 是否参与查询 | 密码、敏感信息等不想被查出来 |
| fill | 自动填充策略 | 创建时间、更新时间等 |
嗯,这里我要重点说一下 exist 属性。我在项目中经常看到有人把一些计算字段、临时变量放在实体类里,但又没加 @TableField(exist = false)。结果 MP 执行 SQL 时,就会报「字段不存在」的错误。你想想看,多冤啊。
我的习惯:只要实体类里有非数据库字段,我第一时间就加上 @TableField(exist = false)。这已经成了我的肌肉记忆了。
还有一个 select 属性,我特别喜欢。比如用户表里的 password 字段,正常情况下查询列表时不应该返回。设置 select = false 后,MP 生成的查询 SQL 会自动排除这个字段,省得你每次都要手动处理。
3.4 @TableLogic:逻辑删除的开关
逻辑删除,说白了就是「假删除」。数据还在数据库里,只是标记为已删除。这个注解就是用来配置这个标记字段的。
@TableName("t_user")
public class User {
@TableId
private Long id;
private String name;
@TableLogic(value = "0", delval = "1")
private Integer deleted;
}
这里有两个关键属性:
- value:未删除时的值,默认是 0。
- delval:已删除时的值,默认是 1。
配置好之后,MP 会自动帮你做两件事:
- 查询时自动加上 WHERE deleted = 0 条件。
- 删除时自动执行 UPDATE 语句,把 deleted 字段设为 1。
我曾经踩过的坑:有一次我忘了配置 @TableLogic,结果调用 deleteById 方法时,数据直接被物理删除了。还好有备份,不然就凉了。所以,我建议你只要涉及删除操作,优先考虑逻辑删除。除非你有明确的物理删除需求。
另外,逻辑删除的字段类型我建议用 Integer 或 tinyint,别用 String。用 String 的话,value 和 delval 要写成 "0" 和 "1",容易搞混。
3.5 注解的综合运用
好了,四个注解都讲完了。咱们来个完整的例子,看看它们怎么配合使用:
@TableName("t_user")
public class User {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@TableField("user_name")
private String name;
@TableField(select = false)
private String password;
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
@TableLogic
private Integer deleted;
@TableField(exist = false)
private String extraInfo;
}
你看,一个实体类配好这些注解,后面写 CRUD 基本就不用操心了。插入时自动填充创建时间,更新时自动填充更新时间,查询时自动过滤已删除的数据,密码字段默认不返回。多省心。
总结一下:
- @TableName:告诉 MP 用哪张表。
- @TableId:告诉 MP 主键是谁,怎么生成。
- @TableField:处理字段映射、排除、自动填充等细节。
- @TableLogic:开启逻辑删除,保护数据安全。
说实话,这几个注解我每天都在用。你只要把它们搞懂了,MyBatis-Plus 就算入门了。下一章咱们聊聊条件构造器,那才是真正让你写 SQL 写到爽的东西。