2026/5/18 19:41:14
网站建设
项目流程
用c 做网站,wordpress把写博客按钮放在前台,网站制作前期所需要准备,网站分享到朋友圈代码做过CRUD Boy的都懂#xff0c;你会发现每个实体类都包含create_time、update_by等重复字段。手动维护这些字段不仅效率低下#xff0c;还容易出错。本文将分享一套经过生产验证的自动化方案#xff0c;涵盖MyBatis-Plus、AOP、JWT等六种核心策略#xff0c;助你彻底摆脱公…做过CRUD Boy的都懂你会发现每个实体类都包含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 ThreadLocalString 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(ListOrder 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编程的结合实现全链路的非阻塞式自动填充。