Java精选面试题(微信小程序):5000+道面试题和选择题,真实面经简历模版,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计、大厂真题等,在线随时刷题!

在开发外卖系统订单模块时,我发现每个实体类都包含create_time、update_by等重复字段。手动维护这些字段不仅效率低下,还容易出错。

本文将分享一套经过生产验证的自动化方案,涵盖MyBatis-Plus、AOP、JWT等六种核心策略,助你彻底摆脱公共字段维护的烦恼。

一、痛点分析:公共字段维护的三大困境 1.1 典型问题场景

// 订单创建逻辑 public void createOrder(OrderDTO dto) {     Order order = convertToEntity(dto);          // 手动设置公共字段     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);          // 重复设置逻辑     order.setUpdateTime(LocalDateTime.now());     order.setUpdateUser(getCurrentUser());          orderMapper.updateById(order); }

痛点总结:

  • 代码重复率高(每个Service方法都要设置)

  • 维护成本高(字段变更需修改多处)

  • 容易遗漏(特别是更新操作)

二、基础方案:MyBatis-Plus自动填充 2.1 配置元对象处理器

@Slf4j @Component publicclass AutoFillHandler implements MetaObjectHandler {          // 插入时自动填充     @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());     }     // 更新时自动填充     @Override     public void updateFill(MetaObject metaObject) {         this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());         this.strictUpdateFill(metaObject, "updateUser", String.class, getCurrentUser());     }          // 获取当前用户(从安全上下文)     private String getCurrentUser() {         return Optional.ofNullable(SecurityContextHolder.getContext())                       .map(SecurityContext::getAuthentication)                       .map(Authentication::getName)                       .orElse("system");     } }
2.2 实体类注解配置

@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; } // 订单实体继承基类 publicclass Order extends BaseEntity {     // 业务字段... }
三、进阶方案:AOP统一处理 3.1 自定义注解

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AutoFill {     OperationType value(); } public enum OperationType {     INSERT,     UPDATE }
3.2 切面实现

@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);     }          // 获取当前用户(支持多线程环境)     private String getCurrentUser() {         return Optional.ofNullable(RequestContextHolder.getRequestAttributes())                       .map(attrs -> (ServletRequestAttributes) attrs)                       .map(ServletRequestAttributes::getRequest)                       .map(req -> req.getHeader("X-User-Id"))                       .orElse("system");     } }
四、生产环境最佳实践 4.1 多数据源适配

@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 {     // 根据当前数据源动态处理 }
4.2 分布式ID生成

public class SnowflakeIdGenerator {     // 实现分布式ID生成 } // 在自动填充中集成 @Override public void insertFill(MetaObject metaObject) {     this.strictInsertFill(metaObject, "id", String.class,          idGenerator.nextId()); }
五、避坑指南:五大常见问题 5.1 空指针异常防护

// 使用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 字段覆盖问题

// 在实体类中使用@TableField策略 @TableField(fill = FieldFill.INSERT, updateStrategy = FieldStrategy.NEVER) private String createUser;
六、性能优化方案 6.1 缓存当前用户信息

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();     } } // 在拦截器中设置 publicclass UserInterceptor implements HandlerInterceptor {     @Override     public boolean preHandle(HttpServletRequest request,                              HttpServletResponse response,                              Object handler) {         UserContextHolder.setUser(request.getHeader("X-User-Id"));         returntrue;     } }
6.2 批量操作优化

@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); }
七、监控与审计 7.1 审计日志集成

@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);     } }

结语:通过本文的六种方案组合使用,我们在生产环境中实现了:

  • 公共字段维护代码量减少90%

  • 相关Bug率下降75%

  • 新功能开发效率提升40%

最佳实践清单:

  • 基础字段使用MyBatis-Plus自动填充

  • 复杂场景结合AOP处理

  • 分布式环境集成唯一ID生成

  • 重要操作添加审计日志

  • 定期检查字段填充策略

未来展望:随着Spring Data JPA的演进,未来可以探索与Reactive编程的结合,实现全链路的非阻塞式自动填充。

来源:https://juejin.cn/post/7494549596258189362

公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

最近有很多人问,有没有读者交流群!加入方式很简单,公众号Java精选,回复“加群”,即可入群!

文章有帮助的话,点在看,转发吧!