MyBatis Plus實(shí)現(xiàn)時(shí)間字段自動(dòng)填充的完整方案
前言
在日常開發(fā)中,我們經(jīng)常需要記錄數(shù)據(jù)的創(chuàng)建時(shí)間和更新時(shí)間。傳統(tǒng)的做法是在每次插入或更新操作時(shí)手動(dòng)設(shè)置這些時(shí)間字段,這種方式不僅繁瑣,還容易遺漏。本文將介紹如何使用 MyBatis Plus 的自動(dòng)填充功能來優(yōu)雅地解決這個(gè)問題。
解決目標(biāo)
- 創(chuàng)建記錄時(shí)自動(dòng)設(shè)置
createTime和updateTime - 更新記錄時(shí)自動(dòng)更新
updateTime,保持createTime不變 - 無需手動(dòng)編寫時(shí)間設(shè)置代碼
- 統(tǒng)一的時(shí)間管理機(jī)制
技術(shù)棧
- 框架: Spring Boot + MyBatis Plus
- 數(shù)據(jù)庫: MySQL
- 實(shí)體管理: JPA 注解 + MyBatis Plus 注解
實(shí)現(xiàn)步驟
1. 實(shí)體類注解配置
首先在實(shí)體類的時(shí)間字段上添加 MyBatis Plus 的自動(dòng)填充注解:
/**
* 郵箱服務(wù)器實(shí)體類
*/
@Entity
@Table(name = "tbl_email_server")
public class EmailServer extends BaseEntity {
// 其他字段...
/** 創(chuàng)建時(shí)間 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/** 更新時(shí)間 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
// getter 和 setter 方法...
}
注解說明:
@TableField(fill = FieldFill.INSERT): 僅在插入操作時(shí)自動(dòng)填充@TableField(fill = FieldFill.INSERT_UPDATE): 在插入和更新操作時(shí)都自動(dòng)填充@JsonFormat: 指定 JSON 序列化時(shí)的日期格式
2. 創(chuàng)建元數(shù)據(jù)處理器
創(chuàng)建自定義的元數(shù)據(jù)處理器來實(shí)現(xiàn)具體的填充邏輯:
package com.ruoyi.framework.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* MyBatis Plus 字段自動(dòng)填充處理器
*
* @author ruoyi
*/
@Component
public class MyBatisMetaObjectHandler implements MetaObjectHandler {
/**
* 插入時(shí)的填充策略
*/
@Override
public void insertFill(MetaObject metaObject) {
Date now = new Date();
// 插入時(shí)同時(shí)填充創(chuàng)建時(shí)間和更新時(shí)間
this.strictInsertFill(metaObject, "createTime", Date.class, now);
this.strictInsertFill(metaObject, "updateTime", Date.class, now);
}
/**
* 更新時(shí)的填充策略
*/
@Override
public void updateFill(MetaObject metaObject) {
// 更新時(shí)僅填充更新時(shí)間
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}
核心方法說明:
insertFill(): 處理插入操作的字段填充updateFill(): 處理更新操作的字段填充strictInsertFill(): 嚴(yán)格插入填充,只有當(dāng)字段值為 null 時(shí)才填充strictUpdateFill(): 嚴(yán)格更新填充,只有當(dāng)字段值為 null 時(shí)才填充
3. 服務(wù)層代碼優(yōu)化
移除原本手動(dòng)設(shè)置時(shí)間的代碼:
修改前:
@Override
public int insertEmailServer(EmailServer emailServer) {
// 手動(dòng)設(shè)置時(shí)間字段
emailServer.setCreateBy(SecurityUtils.getUsername());
emailServer.setCreateTime(new Date()); // ? 需要移除
emailServer.setUpdateTime(new Date()); // ? 需要移除
return emailServerMapper.insertEmailServer(emailServer);
}
@Override
public int updateEmailServer(EmailServer emailServer) {
emailServer.setUpdateBy(SecurityUtils.getUsername());
emailServer.setUpdateTime(new Date()); // ? 需要移除
return emailServerMapper.updateEmailServer(emailServer);
}
修改后:
@Override
public int insertEmailServer(EmailServer emailServer) {
// 只設(shè)置創(chuàng)建者,時(shí)間字段由 MyBatis Plus 自動(dòng)填充
emailServer.setCreateBy(SecurityUtils.getUsername());
return emailServerMapper.insertEmailServer(emailServer);
}
@Override
public int updateEmailServer(EmailServer emailServer) {
// 只設(shè)置更新者,更新時(shí)間由 MyBatis Plus 自動(dòng)填充
emailServer.setUpdateBy(SecurityUtils.getUsername());
return emailServerMapper.updateEmailServer(emailServer);
}
填充機(jī)制詳解
創(chuàng)建記錄時(shí)的填充過程
// 執(zhí)行插入操作
EmailServer emailServer = new EmailServer();
emailServer.setServerName("SMTP服務(wù)器");
// ... 設(shè)置其他業(yè)務(wù)字段
// 調(diào)用 insertEmailServer 時(shí),MyBatis Plus 會(huì):
// 1. 觸發(fā) insertFill 方法
// 2. createTime = 2024-01-15 10:30:25
// 3. updateTime = 2024-01-15 10:30:25 (與創(chuàng)建時(shí)間相同)
更新記錄時(shí)的填充過程
// 執(zhí)行更新操作
EmailServer emailServer = new EmailServer();
emailServer.setServerId(1L);
emailServer.setServerName("新的服務(wù)器名稱");
// ... 修改其他業(yè)務(wù)字段
// 調(diào)用 updateEmailServer 時(shí),MyBatis Plus 會(huì):
// 1. 觸發(fā) updateFill 方法
// 2. createTime = 保持原值不變
// 3. updateTime = 2024-01-15 14:25:30 (更新為當(dāng)前時(shí)間)
數(shù)據(jù)庫建議配置
雖然使用了注解自動(dòng)填充,但建議在數(shù)據(jù)庫層面也設(shè)置默認(rèn)值作為雙重保障:
-- 為時(shí)間字段添加默認(rèn)值 ALTER TABLE tbl_email_server MODIFY COLUMN create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間'; ALTER TABLE tbl_email_server MODIFY COLUMN update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間';
前后對(duì)比
| 對(duì)比項(xiàng) | 手動(dòng)設(shè)置方式 | 自動(dòng)填充方式 |
|---|---|---|
| 代碼量 | 每個(gè)方法都需要設(shè)置 | 一次配置,全局生效 |
| 維護(hù)性 | 容易遺漏,難以統(tǒng)一 | 統(tǒng)一管理,不易出錯(cuò) |
| 一致性 | 依賴開發(fā)者自覺性 | 自動(dòng)保證一致性 |
| 性能 | 無額外性能消耗 | 微小的反射開銷 |
進(jìn)階擴(kuò)展
1. 支持更多字段類型
@Override
public void insertFill(MetaObject metaObject) {
Date now = new Date();
// 支持 Date 類型
this.strictInsertFill(metaObject, "createTime", Date.class, now);
this.strictInsertFill(metaObject, "updateTime", Date.class, now);
// 支持 LocalDateTime 類型
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 支持時(shí)間戳
this.strictInsertFill(metaObject, "createTime", Long.class, System.currentTimeMillis());
}
2. 添加創(chuàng)建者和更新者自動(dòng)填充
@Override
public void insertFill(MetaObject metaObject) {
Date now = new Date();
String currentUser = getCurrentUser(); // 獲取當(dāng)前用戶
// 時(shí)間字段填充
this.strictInsertFill(metaObject, "createTime", Date.class, now);
this.strictInsertFill(metaObject, "updateTime", Date.class, now);
// 用戶字段填充
this.strictInsertFill(metaObject, "createBy", String.class, currentUser);
this.strictInsertFill(metaObject, "updateBy", String.class, currentUser);
}
@Override
public void updateFill(MetaObject metaObject) {
String currentUser = getCurrentUser();
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
this.strictUpdateFill(metaObject, "updateBy", String.class, currentUser);
}
private String getCurrentUser() {
try {
return SecurityUtils.getUsername();
} catch (Exception e) {
return "system"; // 默認(rèn)用戶
}
}
常見問題與解決方案
1. 字段沒有自動(dòng)填充
可能原因:
- 實(shí)體類字段名與數(shù)據(jù)庫列名不匹配
- 忘記添加
@TableField注解 - MetaObjectHandler 沒有被 Spring 管理
解決方案:
// 確保字段名正確映射
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
// 確保處理器被 Spring 管理
@Component // 必須添加此注解
public class MyBatisMetaObjectHandler implements MetaObjectHandler {
// ...
}
2. 更新操作時(shí)創(chuàng)建時(shí)間被意外修改
解決方案:
// 使用 FieldFill.INSERT 而不是 INSERT_UPDATE @TableField(fill = FieldFill.INSERT) // ? 正確 private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) // ? 正確 private Date updateTime;
3. JSON 序列化時(shí)間格式問題
解決方案:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
總結(jié)
MyBatis Plus 的自動(dòng)填充功能為我們提供了一個(gè)優(yōu)雅的解決方案來管理時(shí)間字段:
優(yōu)勢(shì)
- 自動(dòng)化: 無需手動(dòng)編寫重復(fù)的時(shí)間設(shè)置代碼
- 統(tǒng)一性: 全局統(tǒng)一的時(shí)間填充策略
- 可靠性: 避免了人為遺漏設(shè)置時(shí)間的問題
- 擴(kuò)展性: 可以輕松擴(kuò)展到其他字段的自動(dòng)填充
核心實(shí)現(xiàn)
- 在實(shí)體類時(shí)間字段添加
@TableField注解 - 創(chuàng)建實(shí)現(xiàn)
MetaObjectHandler接口的處理器 - 在處理器中實(shí)現(xiàn)
insertFill和updateFill方法 - 移除服務(wù)層中的手動(dòng)時(shí)間設(shè)置代碼
這種方案不僅提升了開發(fā)效率,還增強(qiáng)了代碼的可維護(hù)性和一致性。在實(shí)際項(xiàng)目中,建議結(jié)合數(shù)據(jù)庫默認(rèn)值設(shè)置,形成雙重保障機(jī)制。
以上就是MyBatis Plus實(shí)現(xiàn)時(shí)間字段自動(dòng)填充的完整方案的詳細(xì)內(nèi)容,更多關(guān)于MyBatis Plus時(shí)間字段自動(dòng)填充的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中的深拷貝(深復(fù)制)和淺拷貝(淺復(fù)制)介紹
這篇文章主要介紹了Java中的深拷貝(深復(fù)制)和淺拷貝(淺復(fù)制)介紹,需要的朋友可以參考下2015-03-03
java實(shí)現(xiàn)入棧push和出棧pop過程
文章詳細(xì)介紹了棧的概念、特點(diǎn)以及如何使用數(shù)組和鏈表實(shí)現(xiàn)棧,通過入棧(push)和出棧(pop)操作,展示了棧的數(shù)據(jù)處理過程,并提供了具體的代碼實(shí)現(xiàn)2024-12-12
Spring Boot應(yīng)用事件監(jiān)聽示例詳解
這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用事件監(jiān)聽的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12
mybatis調(diào)用mysql存儲(chǔ)過程并獲取返回值方式
這篇文章主要介紹了mybatis調(diào)用mysql存儲(chǔ)過程并獲取返回值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08
java中將一個(gè)實(shí)體類復(fù)制到另一個(gè)實(shí)體類的3種方法示例
這篇文章主要給大家介紹了關(guān)于java中將一個(gè)實(shí)體類復(fù)制到另一個(gè)實(shí)體類的3種方法,所謂實(shí)體類就是一個(gè)擁有Set和Get方法的類,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
關(guān)于IntelliJ IDEA 打包代碼報(bào)錯(cuò)的問題
這篇文章主要介紹了關(guān)于IntelliJ IDEA 打包代碼報(bào)錯(cuò)的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
idea2020.3配置maven環(huán)境并配置Tomcat的詳細(xì)教程
這篇文章主要介紹了idea2020.3配置maven環(huán)境并配置Tomcat的詳細(xì)教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Java實(shí)現(xiàn)ThreadLocal數(shù)據(jù)在線程池間傳遞的解決方案
隨著業(yè)務(wù)的發(fā)展,系統(tǒng)新增了一個(gè)需求:需要根據(jù)接口請(qǐng)求頭中的特定信息動(dòng)態(tài)選擇數(shù)據(jù)庫實(shí)例進(jìn)行查詢,這個(gè)上下文信息在請(qǐng)求進(jìn)入后被存儲(chǔ)在ThreadLocal中,本文給大家介紹了Java實(shí)現(xiàn)ThreadLocal數(shù)據(jù)在線程池間傳遞的解決方案,需要的朋友可以參考下2025-07-07

