🏆本文收录于「滚雪球学Spring Boot」专栏,专业攻坚指数级提升持续更新中,up!up!up!!
📜 前言
嘿,家人们,跟大家分享一下我的早报,一早我刚挤地铁匆忙来到工作岗位,我那新来的同事就从工位挪了过来,那时候经理还没来,她直接就贴我耳旁悄咪咪的问我:“@TableField注解怎么用?”,我一开始还以为自己百米冲刺导致幻听了,可当她拿我笔记本写给我看的时候,我慌了👻...既然同事有难,那我们就不能袖手旁观,与其任由同事坐等被裁,不如主动出击,授人予渔!帮她度过试用期。择日不如撞日,那今天我们就来聊聊它——@TableField
,顺便给大家也温习一遍,会的同学可以粗略过一遍,不会的同学特别是我那同事!认真看好好学,别走神,因为我只教这一遍。
不可否认,它确实是一位能在实体类和数据库表字段之间“牵线搭桥”的得力助手,特别适合处理数据库和代码不完全对应的场景。😊你曾是否也遇到过数据库字段和实体类字段名称不匹配,或者希望实体类不映射某些数据库字段的情况?@TableField
就是你的不二之选!那么,本期的正文内容即将开始💪。
📚 目录
- 🧐 什么是
@TableField
? - 🎩
@TableField
注解基本用法 - ✍️ 实战演示:@TableField的多种应用场景
- 🔄 动态选择字段:
@TableField(select = false)
- 📝 复杂场景中的 @TableField 配置
- 🛠️
@TableField
和@TableId
的区别 - 💡 最佳实践与注意事项
- 📈 总结
🧐 什么是@TableField
?
@TableField
是MyBatisPlus中的一个注解,用来解决实体类字段和数据库表字段之间的映射问题。其实,说白了,它就是一个专门帮助我们处理“字段小麻烦”的注解。当数据库字段名和实体类属性名不一致时,或者有字段我们不想映射到数据库时,@TableField
就可以派上用场了!✨不信你试试看。
比如,有些数据库字段命名习惯使用下划线风格,而在Java中通常驼峰命名法,这时候如果没有 @TableField
来进行适配,会写很多不必要的代码。接下来下面就来具体感受一下它的妙处所在吧!
🎩 @TableField
注解基本用法
在 @TableField
注解中,最常用的属性就是value
。它用于指定数据库中与实体类字段对应的列名。举个简单的例子,如果数据库中有一个名为user_name
的字段,而在实体类中我们想叫它userName
,这时候就可以用 @TableField
来完成映射:
@TableField("user_name")
private String userName;
这样一来,MyBatisPlus在查询时会自动映射user_name
到userName
,避免手动转换的麻烦,是不是很方便,以前我们可能要手写映射关系,现在一个注解直接搞定。
✍️ 实战演示:@TableField的多种应用场景
既然了解了这个注解是干啥用的,接下来我们通过几个实际的代码示例,展示一下 @TableField
的多种灵活用法,让大家切身感受它的妙用之处。
1. 基本映射:数据库字段名与实体类字段名不一致
假设有一个这样的场景,数据库中有如下表结构:
CREATE TABLE user (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
user_name VARCHAR(30) NOT NULL,
age INT(11),
email VARCHAR(50),
PRIMARY KEY (id)
);
在实体类中,我们希望将 user_name
映射为 userName
,那我们便可以直接使用该注解进行字段名映射:
public class User {
private Long id;
@TableField("user_name")
private String userName;
private Integer age;
private String email;
}
这样一来,我们就不需要在每次查询和保存时手动转换字段名,MyBatisPlus自己就能自动处理这些映射关系,助你解放双手。
2. 忽略字段:不映射数据库中的某些字段
这个场景真是太常见了,有木有??我们不希望实体类中的某些字段映射到数据库,比如计算出的临时属性或客户端特有的字段。这时,我们就可以通过 @TableField(exist = false)
将这些字段标记为“非数据库字段”,这样既可直接以实体类返回且在做数据库交互时又不会映射到SQL语句中,示例代码如下:
public class User {
private Long id;
@TableField("user_name")
private String userName;
private Integer age;
private String email;
@TableField(exist = false) // 此字段不映射到数据库
private String temporaryData;
}
这样,temporaryData
就不会映射到数据库表中,不会影响数据库查询和更新,当你想给前端多返回一个额外的字段,例如temporaryData字段,这样只需要加这么一个注解便可直接搞定。
3. 插入或更新时忽略字段
有时候,我们可能希望字段在插入时或更新时被忽略。比如createTime
字段,我们只在插入时记录创建时间,不希望更新时更改它。这时可以使用@TableField(fill = FieldFill.INSERT)
。示例代码如下:
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
🔄 动态选择字段:@TableField(select = false)
如果有些字段比较敏感,不希望在查询时被默认查出,比如密码字段,可以用@TableField(select = false)
来排除它,防止不必要的暴露:
@TableField(select = false)
private String password;
这样,当执行查询时,password
字段不会出现在查询结果中,但我们可以在特定需求时手动包含它,比如通过自定义查询。
📝 复杂场景中 @TableField 配置
在实际开发中,我们可能会遇到复杂的数据库映射场景,以下是几个 @TableField
高级配置的实际应用示例:
1. 自动填充字段
对于诸如createTime
和updateTime
这样的字段,通常需要自动填充,我们可以通过@TableField(fill = FieldFill.INSERT_UPDATE)
来自动完成插入和更新时的填充:
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
MyBatisPlus会在数据插入或更新时自动赋值,省去手动设置的繁琐步骤。
具体这个大家还需要单独写个MP注入处理器,例如如下:
/**
* bug菌
* MP注入处理器
*/
@Slf4j
public class InjectionMetaObjectHandler implements MetaObjectHandler {
//新增时自动插入
@Override
public void insertFill(MetaObject object) {
try {
if (ObjectUtil.isNotNull(object) && object.getOriginalObject() instanceof BaseEntity) {
BaseEntity entity = (BaseEntity) object.getOriginalObject();
Date date = ObjectUtil.isNotNull(entity.getCreateTime()) ? entity.getCreateTime() : new Date();
String username = StrUtil.isNotBlank(entity.getCreateBy())
? entity.getCreateBy() : LoginHelper.getNickNameNe();
entity.setCreateBy(username);
entity.setUpdateBy(username);
entity.setCreateTime(date);
entity.setUpdateTime(date);
}
} catch (Exception e) {
throw new ServiceException("新增自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}
//更新时自动插入
@Override
public void updateFill(MetaObject object) {
try {
if (ObjectUtil.isNotNull(object) && object.getOriginalObject() instanceof BaseEntity) {
BaseEntity entity = (BaseEntity) object.getOriginalObject();
entity.setUpdateBy(LoginHelper.getUsernameNe());
entity.setUpdateTime(new Date());
}
} catch (Exception e) {
throw new ServiceException("修改自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}
}
示例演示:
这里我将我本地项目直接起来,给大家调用一个接口进行演示一波。大家请看:
代码解析:
如上我定义的这个InjectionMetaObjectHandler 类,它实现了一个基于 MyBatis-Plus 的自动注入处理器,用于实体对象在新增和更新时填充默认的公共字段,如创建时间、更新时间、创建人和更新人。下面是详细解析:
首先我们需要定义一个 InjectionMetaObjectHandler
类且继承 MetaObjectHandler
接口,是一个 MyBatis-Plus 的元对象处理器。
-
新增自动填充逻辑:
insertFill
方法 当进行插入操作时,填充实体对象的创建时间、更新时间、创建人等字段。逻辑步骤:
- 判断
MetaObject
是否不为空,且其getOriginalObject()
返回的对象是BaseEntity
类型的实例。 - 如果实体的
createTime
不为空,使用已有时间;否则,使用当前时间(new Date()
)。 - 创建人字段
createBy
:- 如果实体中已经设置了值(非空字符串),则使用该值;
- 否则调用
LoginHelper.getNickNameNe()
获取当前登录用户的昵称。
- 设置
BaseEntity
的以下字段:createBy
和updateBy
都设为创建人用户名。createTime
和updateTime
都设为创建时间。
异常处理:
- 捕获异常并抛出自定义
ServiceException
,同时指定 HTTP 状态码。
- 判断
-
更新自动填充逻辑:
updateFill
方法 当进行更新操作时,填充实体对象的更新时间和更新人。逻辑步骤:
- 同样检查
MetaObject
是否不为空,且其getOriginalObject()
返回的对象是BaseEntity
类型的实例。 - 更新人字段
updateBy
:- 直接调用
LoginHelper.getUsernameNe()
获取当前登录用户的用户名。
- 直接调用
- 更新时间字段
updateTime
:- 设为当前时间(
new Date()
)。
- 设为当前时间(
异常处理:
- 捕获异常并抛出自定义
ServiceException
,指定 HTTP 状态码。
- 同样检查
-
工具类的使用
ObjectUtil
:判断对象是否非空。StrUtil
:判断字符串是否非空白。LoginHelper
:获取当前登录用户的相关信息(如昵称、用户名)。BaseEntity
:是代码中表示数据库基础实体的类,包含了常见的公共字段,如创建时间、更新时间等。ServiceException
:自定义异常类,用于抛出业务异常。HttpStatus.HTTP_UNAUTHORIZED
:表示 HTTP 状态码 401(未授权)。
代码功能说明
该类的核心目的是在 MyBatis-Plus 操作数据库时,通过自动填充机制统一处理实体类的公共字段,目的是为了减少手动赋值的繁琐步骤,确保数据的统一性和完整性,就凭这点,大家不用它都说不过去了吧。
这种设计说实话非常适合业务系统中需要频繁记录“谁创建了数据”“谁更新了数据”等场景,且通过异常机制处理了可能的意外情况,提高了代码的健壮性,审计信息也不需要耦合进业务层面中去了,简直就是谁用谁爽,这真不是我吹,必须包的!
2. 动态查询控制
如果有些字段只在特定条件下查询,我们可以通过@TableField(condition = SqlCondition.LIKE)
设置字段的查询条件:
@TableField(condition = SqlCondition.LIKE)
private String email;
这样,email
字段在查询时会自动使用LIKE查询,适合需要模糊查询的场景,这个场景倒是用得不多,毕竟MP提供的Wrapper条件构造器已经够全面了。
🛠️ @TableField
和 @TableId
的区别
虽然 @TableField
和 @TableId
都用于字段的映射,但它们在功能上有着显著区别,如下:
@TableId
:主要用于主键字段的映射,指定表中的主键列。@TableField
:用于非主键字段的映射,灵活配置字段属性。
例如,如果id
是主键,我们可以这样定义:
@TableId
private Long id;
而其他字段则通过@TableField
来管理,如userName
、email
等。
💡 最佳实践与注意事项
-
保持字段命名一致:尽量在数据库和实体类中保持一致的命名风格,避免过多
@TableField
配置,减少维护成本。 -
灵活应用
select
与exist
属性: 在查询时控制字段的选择,既能提高安全性,也能提升性能。 -
使用自动填充优化字段管理:对于需要自动更新的时间戳字段,尽量使用
FieldFill.INSERT_UPDATE
等自动填充功能。 -
定期更新数据库映射:如果数据库表结构变更,及时同步更新实体类和
@TableField
映射,确保映射关系正确。
📈 总结
ok,说到这儿,本期即将要接近尾声啦,在结束之前,我来简单讲两句:@TableField
它是MyBatis-Plus提供的一个灵活且强大的注解,专为那些不希望或不方便直接映射的字段而生。通过灵活配置 @TableField
注解,我们可以在数据库字段和Java对象之间创建清晰且稳定的映射关系,减少代码和数据库耦合度。
在实际项目中,@TableField
的应用可以帮助我们更高效地进行代码维护和管理,减少不必要的代码输出。希望今天的分享能够让你对 @TableField
有一个更全面的理解,有需要的小伙伴们赶紧在你的项目中试试吧!🎉
希望这篇理论+实战保姆级教程能够真正帮助到大家,真正掌握@TableField
的妙用核心,能够在日常crud中“如鱼得水”!
📣 关于我
我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。
-End-