当前位置: 首页 > news >正文

SpringBoot - 公共字段自动填充的6种方案

在这里插入图片描述

01痛点速览

// 每写一个 Service 就要来一遍
order.setCreateTime(LocalDateTime.now());
order.setUpdateTime(LocalDateTime.now());
order.setCreateUser(getCurrentUser());
order.setUpdateUser(getCurrentUser());

总结:

  1. Ctrl C + Ctrl V 到手抽筋
  2. 一改字段,全局爆炸
  3. 一不留神就漏填

02方案 1MyBatis-Plus 元对象处理器

最香基础款

2.1 核心代码

@Slf4j
@Component
public class AutoFillHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {// 新增时填充strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());strictInsertFill(metaObject, "createUser", String.class, getCurrentUser());strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());strictInsertFill(metaObject, "updateUser", String.class, getCurrentUser());}@Overridepublic void updateFill(MetaObject metaObject) {// 更新时只刷新 update*strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());strictUpdateFill(metaObject, "updateUser", String.class, getCurrentUser());}/** 从 Security 上下文里拿登录人 */private String getCurrentUser() {return Optional.ofNullable(SecurityContextHolder.getContext()).map(SecurityContext::getAuthentication).map(Authentication::getName).orElse("system");}
}

注解:

  1. 实现 MetaObjectHandler,MyBatis-Plus 会自动回调
  2. strictInsertFill 只在 insert 语句里生效
  3. 使用 Optional 防 NPE,稳!

2.2 实体类配上注解

@Data
public abstract class 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;
}@Entity
@Table(name = "t_order")
public class Order extends BaseEntity { }

03方案 2:AOP 切面

专治各种不服

3.1 自定义注解,声明式零侵入

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoFill {OperationType value();   // INSERT / UPDATE
}public enum OperationType {INSERT, UPDATE
}

3.2 切面逻辑

@Aspect
@Component
@Slf4j
public class AutoFillAspect {@Around("@annotation(autoFill)")public Object around(ProceedingJoinPoint pjp, AutoFill autoFill) throws Throwable {Object[] args = pjp.getArgs();for (Object arg : args) {if (arg instanceof BaseEntity) {fill((BaseEntity) arg, autoFill.value());}}return pjp.proceed(args);}private void fill(BaseEntity entity, OperationType type) {String user = getCurrentUser();LocalDateTime now = LocalDateTime.now();if (type == OperationType.INSERT) {entity.setCreateTime(now);entity.setCreateUser(user);}entity.setUpdateTime(now);entity.setUpdateUser(user);}private String getCurrentUser() {return Optional.ofNullable(RequestContextHolder.getRequestAttributes()).map(ServletRequestAttributes.class::cast).map(ServletRequestAttributes::getRequest).map(req -> req.getHeader("X-User-Id")).orElse("system");}
}

注解:

  1. @Around 拦截所有标注 @AutoFill 的方法
  2. 统一在进入方法前就把字段塞满
  3. 支持多参数批量处理,一行注解搞定

04方案 3:分布式 ID + 多数据源

4.1 Snowflake 发号器

@Configuration
public class SnowflakeConfig {@Beanpublic SnowflakeIdWorker idWorker(@Value("${snowflake.workerId}") long workerId,@Value("${snowflake.dataCenterId}") long dataCenterId) {return new SnowflakeIdWorker(workerId, dataCenterId);}
}

4.2 多数据源下自动填充

public class MultiDataSourceAutoFillHandler extends MetaObjectHandler {@Autowiredprivate SnowflakeIdWorker idWorker;@Overridepublic void insertFill(MetaObject metaObject) {super.insertFill(metaObject);// 分布式主键strictInsertFill(metaObject, "id", Long.class, idWorker.nextId());}
}

05方案 4:性能优化三板斧

5.1 ThreadLocal 缓存当前用户

public class UserContextHolder {private static final ThreadLocal<String> holder = new ThreadLocal<>();public static void set(String user) { holder.set(user); }public static String get() { return holder.get(); }public static void clear() { holder.remove(); }
}public class UserInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) {UserContextHolder.set(req.getHeader("X-User-Id"));return true;}@Overridepublic void afterCompletion(...) {UserContextHolder.clear();   // 防止内存泄漏}
}

5.2 批量操作统一时间

@Transactional
public void batchInsert(List<Order> orders) {String user = UserContextHolder.get();LocalDateTime now = LocalDateTime.now();orders.forEach(o -> {o.setCreateTime(now);o.setCreateUser(user);o.setUpdateTime(now);o.setUpdateUser(user);});orderMapper.batchInsert(orders);
}

06方案 5:监控 + 审计

6.1 切面记录操作日志

@Aspect
@Component
public class OperationLogAspect {@AfterReturning("@annotation(autoFill)")public void log(AutoFill autoFill) {LogEntry log = new LogEntry();log.setOperator(UserContextHolder.get());log.setOperation(autoFill.value().name());log.setTime(LocalDateTime.now());logService.save(log);}
}

避坑指南

在这里插入图片描述

总结

MyBatis-Plus 打底,AOP 做加持,Snowflake 管 ID,ThreadLocal 提性能,切面搞审计

http://www.dtcms.com/a/341279.html

相关文章:

  • 使用安卓平板,通过USB数据线(而不是Wi-Fi)来控制电脑(版本1)
  • Mac编译Android AOSP
  • Vue2+Vue3前端开发_Day3
  • vue3中,如何解决数字精度问题(big.js的使用)
  • 计算机毕设Spark项目实战:基于大数据技术的就业数据分析系统Django+Vue开发指南
  • SQL count(*)与 sum 区别
  • 【iOS】NSRunLoop
  • Preprocessing Model in MPC 2 - 背景、基础原语和Beaver三元组
  • 计算机网络--HTTP协议
  • Jenkins服务器配置SSH
  • 强制重启导致Ubuntu24.04LTS amd的WIFI无法使用的解决方案
  • 超长视频生成新突破!LongVie框架问世,创作不再受时长限制
  • spring第9课,spring对DAO的支持
  • C语言---编译的最小单位---令牌(Token)
  • 基于 Java 调用泛微 OA WebService 创建表单流程
  • 如何保障内部网络安全前提下,实现与外部互联网之间的文件传输?
  • 一种融合AI与OCR的施工许可证识别技术,提升工程监管效率,实现自动化、精准化处理。
  • 【CUDA编程】CUDA编程入门第一课
  • QT聊天项目DAY20
  • 【unitrix数间混合计算】3.3 无符号整数标记trait(bin_unsigned.rs)
  • C++:仿函数部分的补充、模版进阶(非类型模版参数、模板的特化、模板的分离编译)
  • 1277. 统计全为 1 的正方形子矩阵
  • 含钼溶液中回收钼
  • 【Windows】Windows平台基于加速地址安装vcpkg并集成到Visual Studio 2017
  • MySQL 50 道经典练习题及答案
  • 使用Jmeter轻松实现AES加密测试
  • 国电南自面试记录
  • 细说数仓中不同类型的维度
  • 实时交互世界模型新标杆! Skywork AI 发布 Matrix-Game 2.0: 攻克实时交互难题,在实时性与视频生成质量上实现跨越式突破!
  • synchronized锁,ReentrantLock 锁