Java精選面試題(微信小程序):5000+道面試題和選擇題,真實(shí)面經(jīng),簡(jiǎn)歷模版,包含Java基礎(chǔ)、并發(fā)、JVM、線程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架構(gòu)設(shè)計(jì)、大廠真題等,在線隨時(shí)刷題!
在開發(fā)外賣系統(tǒng)訂單模塊時(shí),我發(fā)現(xiàn)每個(gè)實(shí)體類都包含create_time、update_by等重復(fù)字段。手動(dòng)維護(hù)這些字段不僅效率低下,還容易出錯(cuò)。
本文將分享一套經(jīng)過生產(chǎn)驗(yàn)證的自動(dòng)化方案,涵蓋MyBatis-Plus、AOP、JWT等六種核心策略,助你徹底擺脫公共字段維護(hù)的煩惱。
一、痛點(diǎn)分析:公共字段維護(hù)的三大困境 1.1 典型問題場(chǎng)景
// 訂單創(chuàng)建邏輯 public void createOrder(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); } // 訂單更新邏輯 public void updateOrder(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ù)成本高(字段變更需修改多處)
容易遺漏(特別是更新操作)
@Slf4j @Component publicclass AutoFillHandler implements MetaObjectHandler { // 插入時(shí)自動(dòng)填充 @Override public void insertFill(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 public void updateFill(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í)體類注解配置@Data publicclass BaseEntity { @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í)體繼承基類 publicclass Order extends BaseEntity { // 業(yè)務(wù)字段... }三、進(jìn)階方案:AOP統(tǒng)一處理 3.1 自定義注解@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AutoFill { OperationType value(); } public enum OperationType { INSERT, UPDATE }3.2 切面實(shí)現(xiàn)@Aspect @Component @Slf4j publicclass AutoFillAspect { @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); } private void fillFields(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 publicclass DataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean public MetaObjectHandler metaObjectHandler() { returnnew MultiDataSourceAutoFillHandler(); } } publicclass MultiDataSourceAutoFillHandler extends MetaObjectHandler { // 根據(jù)當(dāng)前數(shù)據(jù)源動(dòng)態(tài)處理 }4.2 分布式ID生成public class SnowflakeIdGenerator { // 實(shí)現(xiàn)分布式ID生成 } // 在自動(dòng)填充中集成 @Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, "id", String.class, idGenerator.nextId()); }五、避坑指南:五大常見問題 5.1 空指針異常防護(hù)// 使用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 字段覆蓋問題// 在實(shí)體類中使用@TableField策略 @TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER) private String createUser;六、性能優(yōu)化方案 6.1 緩存當(dāng)前用戶信息public class UserContextHolder { privatestaticfinal ThreadLocal userHolder = new ThreadLocal<>(); public static void setUser(String user) { userHolder.set(user); } public static String getUser() { return userHolder.get(); } public static void clear() { userHolder.remove(); } } // 在攔截器中設(shè)置 publicclass UserInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { UserContextHolder.setUser(request.getHeader("X-User-Id")); returntrue; } }6.2 批量操作優(yōu)化@Transactional public void batchInsert(List 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) public class BaseEntity { @CreatedBy private String createUser; @LastModifiedBy private String updateUser; @CreatedDate private LocalDateTime createTime; @LastModifiedDate private LocalDateTime updateTime; }7.2 操作日志追蹤@Aspect @Component public class OperationLogAspect { @AfterReturning("@annotation(autoFill)") public void logOperation(AutoFill autoFill) { LogEntry log = new LogEntry(); log.setOperator(getCurrentUser()); log.setOperationType(autoFill.value().name()); logService.save(log); } }結(jié)語(yǔ):通過本文的六種方案組合使用,我們?cè)谏a(chǎn)環(huán)境中實(shí)現(xiàn)了:
公共字段維護(hù)代碼量減少90%
相關(guān)Bug率下降75%
新功能開發(fā)效率提升40%
最佳實(shí)踐清單:
基礎(chǔ)字段使用MyBatis-Plus自動(dòng)填充
復(fù)雜場(chǎng)景結(jié)合AOP處理
分布式環(huán)境集成唯一ID生成
重要操作添加審計(jì)日志
定期檢查字段填充策略
未來展望:隨著Spring Data JPA的演進(jìn),未來可以探索與Reactive編程的結(jié)合,實(shí)現(xiàn)全鏈路的非阻塞式自動(dòng)填充。
來源:https://juejin.cn/post/7494549596258189362
公眾號(hào)“Java精選”所發(fā)表內(nèi)容注明來源的,版權(quán)歸原出處所有(無法查證版權(quán)的或者未注明出處的均來自網(wǎng)絡(luò),系轉(zhuǎn)載,轉(zhuǎn)載的目的在于傳遞更多信息,版權(quán)屬于原作者。如有侵權(quán),請(qǐng)聯(lián)系,筆者會(huì)第一時(shí)間刪除處理!
最近有很多人問,有沒有讀者交流群!加入方式很簡(jiǎn)單,公眾號(hào)Java精選,回復(fù)“加群”,即可入群!
文章有幫助的話,點(diǎn)在看,轉(zhuǎn)發(fā)吧!
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.