消除重復(fù)代碼:MyBatis-Plus自動(dòng)填充公共字段實(shí)戰(zhàn)

消除重復(fù)代碼:MyBatis-Plus自動(dòng)填充公共字段實(shí)戰(zhàn)

大家好,我是凱哥Java

本文標(biāo)簽:自動(dòng)填充、Java開(kāi)發(fā)技巧、性能優(yōu)化、公共字段填充

在日常的開(kāi)發(fā)中,我們經(jīng)常能碰見(jiàn)每個(gè)實(shí)體類(lèi)都包含create_time、update_by等重復(fù)字段。手動(dòng)維護(hù)這些字段不僅效率低下,還容易出錯(cuò)。本文將分享一套經(jīng)過(guò)生產(chǎn)驗(yàn)證的自動(dòng)化方案,涵蓋MyBatis-Plus、AOP、JWT等六種核心策略,助你徹底擺脫公共字段維護(hù)的煩惱。

本文深入探討了如何利用MyBatis-Plus、AOP等技術(shù)實(shí)現(xiàn)數(shù)據(jù)庫(kù)表公共字段(如創(chuàng)建時(shí)間、更新用戶)的自動(dòng)化維護(hù),顯著減少代碼重復(fù)率和維護(hù)成本。涵蓋基礎(chǔ)方案到高級(jí)實(shí)踐,并提供性能優(yōu)化與監(jiān)控審計(jì)策略。

一、痛點(diǎn)分析:公共字段維護(hù)的三大困境

1.1 典型問(wèn)題場(chǎng)景

// 訂單創(chuàng)建邏輯

publicvoidcreateOrder(OrderDTO dto){

? ? Order order = convertToEntity(dto);


? ? // 手動(dòng)設(shè)置公共字段

? ? order.setCreateTime(LocalDateTime.now());

? ? order.setUpdateTime(LocalDateTime.now());

? ? order.setCreateUser(getCurrentUser());

? ? order.setUpdateUser(getCurrentUser());


? ? orderMapper.insert(order);

}

// 訂單更新邏輯?

publicvoidupdateOrder(OrderDTO dto){

? ? Order order = convertToEntity(dto);


? ? // 重復(fù)設(shè)置邏輯

? ? order.setUpdateTime(LocalDateTime.now());

? ? order.setUpdateUser(getCurrentUser());


? ? orderMapper.updateById(order);

}

痛點(diǎn)總結(jié):

代碼重復(fù)率高(每個(gè)Service方法都要設(shè)置)

維護(hù)成本高(字段變更需修改多處)

容易遺漏(特別是更新操作)

二、基礎(chǔ)方案:MyBatis-Plus自動(dòng)填充

2.1 配置元對(duì)象處理器

@Slf4j

@Component

publicclassAutoFillHandlerimplementsMetaObjectHandler{


? ? // 插入時(shí)自動(dòng)填充

? ? @Override

? ? publicvoidinsertFill(MetaObject metaObject){

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

? ? ? ? this.strictInsertFill(metaObject, "createUser", String.class, getCurrentUser());

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

? ? ? ? this.strictUpdateFill(metaObject, "updateUser", String.class, getCurrentUser());

? ? }

? // 更新時(shí)自動(dòng)填充

? ? @Override

? ? publicvoidupdateFill(MetaObject metaObject){

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

? ? ? ? this.strictUpdateFill(metaObject, "updateUser", String.class, getCurrentUser());

? ? }


// 獲取當(dāng)前用戶(從安全上下文)

? ? private String getCurrentUser(){

? ? ? ? return Optional.ofNullable(SecurityContextHolder.getContext())

? ? ? ? ? ? ? ? ? ? ? .map(SecurityContext::getAuthentication)

? ? ? ? ? ? ? ? ? ? ? .map(Authentication::getName)

? ? ? ? ? ? ? ? ? ? ? .orElse("system");

? ? }

}

2.2 實(shí)體類(lèi)注解配置

@Data

publicclassBaseEntity{

? ? @TableField(fill = FieldFill.INSERT)

? ? private LocalDateTime createTime;


? ? @TableField(fill = FieldFill.INSERT_UPDATE)

? ? private LocalDateTime updateTime;


? ? @TableField(fill = FieldFill.INSERT)

? ? private String createUser;


? ? @TableField(fill = FieldFill.INSERT_UPDATE)

? ? private String updateUser;

}

// 訂單實(shí)體繼承基類(lèi)

publicclassOrderextendsBaseEntity{

? ? // 業(yè)務(wù)字段...

}

、進(jìn)階方案:AOP統(tǒng)一處理

3.1 自定義注解

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public@interface AutoFill {

? ? OperationType value();

}

publicenum OperationType {

? ? INSERT,

? ? UPDATE

}

3.2 切面實(shí)現(xiàn)

@Aspect

@Component

@Slf4j

publicclassAutoFillAspect{


? ? @Autowired

? ? private ObjectMapper objectMapper;

? ? @Around("@annotation(autoFill)")

? ? public Object around(ProceedingJoinPoint pjp, AutoFill autoFill)throws Throwable {

? ? ? ? Object[] args = pjp.getArgs();

? ? ? ? for (Object arg : args) {

? ? ? ? ? ? if (arg instanceof BaseEntity) {

? ? ? ? ? ? ? ? fillFields((BaseEntity) arg, autoFill.value());

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? return pjp.proceed(args);

? ? }

? ? privatevoidfillFields(BaseEntity entity, OperationType type){

? ? ? ? String currentUser = getCurrentUser();

? ? ? ? LocalDateTime now = LocalDateTime.now();


? ? ? ? if (type == OperationType.INSERT) {

? ? ? ? ? ? entity.setCreateTime(now);

? ? ? ? ? ? entity.setCreateUser(currentUser);

? ? ? ? }

? ? ? ? entity.setUpdateTime(now);

? ? ? ? entity.setUpdateUser(currentUser);

? ? }


? ? // 獲取當(dāng)前用戶(支持多線程環(huán)境)

? ? private String getCurrentUser(){

? ? ? ? return Optional.ofNullable(RequestContextHolder.getRequestAttributes())

? ? ? ? ? ? ? ? ? ? ? .map(attrs -> (ServletRequestAttributes) attrs)

? ? ? ? ? ? ? ? ? ? ? .map(ServletRequestAttributes::getRequest)

? ? ? ? ? ? ? ? ? ? ? .map(req -> req.getHeader("X-User-Id"))

? ? ? ? ? ? ? ? ? ? ? .orElse("system");

? ? }

}

四、生產(chǎn)環(huán)境最佳實(shí)踐

4.1 多數(shù)據(jù)源適配

@Configuration

publicclassDataSourceConfig{


? ? @Bean

? ? @ConfigurationProperties("spring.datasource.master")

? ? public DataSource masterDataSource(){

? ? ? ? return DataSourceBuilder.create().build();

? ? }


? ? @Bean

? ? public MetaObjectHandler metaObjectHandler(){

? ? ? ? returnnew MultiDataSourceAutoFillHandler();

? ? }

}

publicclassMultiDataSourceAutoFillHandlerextendsMetaObjectHandler{

? ? // 根據(jù)當(dāng)前數(shù)據(jù)源動(dòng)態(tài)處理

}

4.2 分布式ID生成

publicclassSnowflakeIdGenerator{

? ? // 實(shí)現(xiàn)分布式ID生成

}

// 在自動(dòng)填充中集成

@Override

publicvoidinsertFill(MetaObject metaObject){

? ? this.strictInsertFill(metaObject, "id", String.class,?

? ? ? ? idGenerator.nextId());

}

五、避坑指南:五大常見(jiàn)問(wèn)題

// 使用Optional處理可能為空的情況

private String safeGetUser(){

? ? return Optional.ofNullable(SecurityContextHolder.getContext())

? ? ? ? ? ? ? ? ?.map(SecurityContext::getAuthentication)

? ? ? ? ? ? ? ? ?.map(Authentication::getPrincipal)

? ? ? ? ? ? ? ? ?.map(principal -> {

? ? ? ? ? ? ? ? ? ? ?if (principal instanceof UserDetails) {

? ? ? ? ? ? ? ? ? ? ? ? ?return ((UserDetails) principal).getUsername();

? ? ? ? ? ? ? ? ? ? ?}

? ? ? ? ? ? ? ? ? ? ?return principal.toString();

? ? ? ? ? ? ? ? ?})

? ? ? ? ? ? ? ? ?.orElse("system");

}

5.2 字段覆蓋問(wèn)題

// 在實(shí)體類(lèi)中使用@TableField策略

@TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER)

private String createUser;

六、性能優(yōu)化方案

6.1 緩存當(dāng)前用戶信息

publicclassUserContextHolder{

? ? privatestaticfinal ThreadLocal<String> userHolder = new ThreadLocal<>();


? ? publicstaticvoidsetUser(String user){

? ? ? ? userHolder.set(user);

? ? }


? ? publicstatic String getUser(){

? ? ? ? return userHolder.get();

? ? }


? ? publicstaticvoidclear(){

? ? ? ? userHolder.remove();

? ? }

}

// 在攔截器中設(shè)置

publicclassUserInterceptorimplementsHandlerInterceptor{

? ? @Override

? ? publicbooleanpreHandle(HttpServletRequest request,?

? ? ? ? ? ? ? ? ? ? ? ? ? ? HttpServletResponse response,?

? ? ? ? ? ? ? ? ? ? ? ? ? ? Object handler){

? ? ? ? UserContextHolder.setUser(request.getHeader("X-User-Id"));

? ? ? ? returntrue;

? ? }

}

6.2 批量操作優(yōu)化

@Transactional

publicvoidbatchInsert(List<Order> orders){

? ? // 提前獲取公共字段值

? ? String user = getCurrentUser();

? ? LocalDateTime now = LocalDateTime.now();


? ? orders.forEach(order -> {

? ? ? ? order.setCreateTime(now);

? ? ? ? order.setCreateUser(user);

? ? ? ? order.setUpdateTime(now);

? ? ? ? order.setUpdateUser(user);

? ? });


? ? orderMapper.batchInsert(orders);

}

七、監(jiān)控與審計(jì)

7.1 審計(jì)日志集成

@EntityListeners(AuditingEntityListener.class)

publicclassBaseEntity{

? ? @CreatedBy

? ? private String createUser;


? ? @LastModifiedBy

? ? private String updateUser;


? ? @CreatedDate

? ? private LocalDateTime createTime;


? ? @LastModifiedDate

? ? private LocalDateTime updateTime;

}

7.2 操作日志追蹤

@Aspect

@Component

publicclassOperationLogAspect{


? ? @AfterReturning("@annotation(autoFill)")

? ? publicvoidlogOperation(AutoFill autoFill){

? ? ? ? LogEntry log = new LogEntry();

? ? ? ? log.setOperator(getCurrentUser());

? ? ? ? log.setOperationType(autoFill.value().name());

? ? ? ? logService.save(log);

? ? }

}

結(jié)語(yǔ):?通過(guò)本文的六種方案組合使用,我們?cè)谏a(chǎn)環(huán)境中實(shí)現(xiàn)了:

公共字段維護(hù)代碼量減少90%

相關(guān)Bug率下降75%

新功能開(kāi)發(fā)效率提升40%

最佳實(shí)踐清單:

基礎(chǔ)字段使用MyBatis-Plus自動(dòng)填充

復(fù)雜場(chǎng)景結(jié)合AOP處理

分布式環(huán)境集成唯一ID生成

重要操作添加審計(jì)日志

定期檢查字段填充策略

未來(lái)展望:?隨著Spring Data JPA的演進(jìn),未來(lái)可以探索與Reactive編程的結(jié)合,實(shí)現(xiàn)全鏈路的非阻塞式自動(dòng)填充。

Java開(kāi)發(fā)技巧

數(shù)據(jù)庫(kù)字段管理

代碼質(zhì)量提升

Spring Boot實(shí)戰(zhàn)

系統(tǒng)模塊優(yōu)化

作者:凱哥Java

類(lèi)型:轉(zhuǎn)載

原作者:小廠永遠(yuǎn)得不到的男人

日期:2025年07月16日

標(biāo)簽:Mybaits-Plus、AOP應(yīng)用、性能優(yōu)化技巧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容