Java可通过反射扫描实体类字段,自动识别@CreatedDate和@LastModifiedDate注解,在JDBC插入/更新前动态填充时间值;需用setAccessible(true)访问私有字段,建议缓存字段提升性能,并根据主键是否为空区分插入(两者均赋值)与更新(仅赋值@LastModifiedDate)。

Java 中可以通过反射扫描实体类字段,自动识别 @CreatedDate 和 @LastModifiedDate 注解,并在 JDBC 插入/更新前动态填充时间值,从而避免手动设置时间字段的样板代码。
识别带时间注解的字段
使用反射遍历类的所有声明字段(包括 private 字段),检查是否标注了 Spring Data JPA 提供的 @CreatedDate 或 @LastModifiedDate。注意需启用 AccessibleObject.setAccessible(true) 才能访问私有字段。
- 调用
clazz.getDeclaredFields()获取全部字段 - 对每个字段调用
field.isAnnotationPresent(CreatedDate.class)或isAnnotationPresent(LastModifiedDate.class) - 建议缓存已扫描过的类及其注解字段(如用
ConcurrentHashMap<Class, List<Field>>),避免重复反射开销
区分插入与更新场景并赋值
仅靠注解无法自动判断是 insert 还是 update,需结合业务上下文或额外标记(如主键是否为空)。通常逻辑是:
-
插入时:为
@CreatedDate和@LastModifiedDate字段都赋当前时间 -
更新时:只更新
@LastModifiedDate字段,@CreatedDate保持原值不变 - 可借助主键(如
id == null || id )粗略判断新记录;更稳妥的方式是显式传入操作类型枚举
集成到 JDBC 参数预处理流程
在执行 PreparedStatement 前,对实体对象做一次反射赋值,再提取字段值用于 SQL 绑定。推荐封装成工具方法:
立即学习“Java免费学习笔记(深入)”;
- 定义方法
void fillAuditFields(Object entity, boolean isInsert) - 内部遍历缓存的注解字段列表,根据
isInsert决定是否写入@CreatedDate - 支持
java.time.LocalDateTime、java.util.Date、Long(毫秒时间戳)等常见类型,用field.getType().isAssignableFrom(...)判断后安全赋值 - 避免直接修改原始对象字段——若需不可变语义,可返回新副本或使用 Builder 模式
注意事项与替代思路
纯 JDBC 场景下反射注入审计字段可行,但要注意几点:
- Spring Data JPA 的
@CreatedDate默认依赖AuditingEntityListener和@EnableJpaAuditing,这在纯 JDBC 中不生效,必须手动实现 - 字段类型需统一或做好兼容(例如
LocalDateTime.now()赋给Date字段需转换) - 若项目已用 MyBatis,可考虑用
@Intercepts拦截器 + 反射,在Executor层统一处理;Hibernate 则优先用@PrePersist/@PreUpdate - 生产环境建议配合单元测试验证不同字段类型、null 安全性及并发场景下的行为


















