SpringData JPA审计@Embeddable集合失效问题及解决方案咨询
问题原因
Spring Data JPA 2.7.x版本的审计机制(由@EnableJpaAuditing驱动)仅针对标注@Entity的实体类生效。你用@ElementCollection维护的可嵌入类B属于@Embeddable类型,并非独立实体——它没有自己的JPA生命周期回调(比如persist/merge事件),所以Spring Data JPA的审计组件不会自动为其填充@CreatedBy、@CreatedDate等字段,审计逻辑只会触发在根实体A上。
替代审计方案
方案1:手动在实体生命周期回调中填充
在实体A中添加@PrePersist和@PreUpdate注解的方法,手动遍历集合中的B对象,填充审计字段。需要注入AuditorAware来获取当前操作用户,示例代码如下:
@Entity public class A { @Id private Long id; @ElementCollection private List<B> bList = new ArrayList<>(); @Autowired private AuditorAware<String> auditorAware; @PrePersist public void prePersist() { String currentUser = auditorAware.getCurrentAuditor().orElse("unknown"); LocalDateTime now = LocalDateTime.now(); // 填充集合中B的审计字段 bList.forEach(b -> { b.getAuditFields().setCreatedBy(currentUser); b.getAuditFields().setCreatedDate(now); b.getAuditFields().setLastModifiedBy(currentUser); b.getAuditFields().setLastModifiedDate(now); }); } @PreUpdate public void preUpdate() { String currentUser = auditorAware.getCurrentAuditor().orElse("unknown"); LocalDateTime now = LocalDateTime.now(); bList.forEach(b -> { b.getAuditFields().setLastModifiedBy(currentUser); b.getAuditFields().setLastModifiedDate(now); }); } }
方案2:将可嵌入类B改为独立实体
把B从@Embeddable改为@Entity,添加主键字段,然后用@OneToMany关联实体A。这样B作为独立实体,就能被Spring Data JPA的审计机制自动处理:
// 修改后的B类 @Entity public class B { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Embedded private AuditFields auditFields; @ManyToOne(fetch = FetchType.LAZY) private A a; // getter/setter } // 修改后的A类 @Entity public class A { @Id private Long id; @OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true) private List<B> bList = new ArrayList<>(); // 添加B时记得设置关联关系 public void addB(B b) { b.setA(this); this.bList.add(b); } }
注意:这个方案会改变数据库结构,原来的ElementCollection关联表会变成独立的B实体表,需要考虑数据迁移的成本。
方案3:自定义JPA实体监听器
创建一个通用的实体监听器,监听实体A的生命周期事件,在事件触发时更新集合中B的审计字段:
public class AuditListener { @Autowired private AuditorAware<String> auditorAware; @PrePersist public void prePersist(Object entity) { if (entity instanceof A) { A a = (A) entity; String currentUser = auditorAware.getCurrentAuditor().orElse("unknown"); LocalDateTime now = LocalDateTime.now(); a.getBList().forEach(b -> { b.getAuditFields().setCreatedBy(currentUser); b.getAuditFields().setCreatedDate(now); b.getAuditFields().setLastModifiedBy(currentUser); b.getAuditFields().setLastModifiedDate(now); }); } } @PreUpdate public void preUpdate(Object entity) { if (entity instanceof A) { A a = (A) entity; String currentUser = auditorAware.getCurrentAuditor().orElse("unknown"); LocalDateTime now = LocalDateTime.now(); a.getBList().forEach(b -> { b.getAuditFields().setLastModifiedBy(currentUser); b.getAuditFields().setLastModifiedDate(now); }); } } } // 在实体A上注册监听器 @Entity @EntityListeners(AuditListener.class) public class A { // ... 原有字段 }
内容的提问来源于stack exchange,提问作者Armer B.
相关产品推荐
相关产品推荐

