Spring JPA保存对象时如何避免关联Key对象被间接更新?
我定义了Credential实体类,其中包含关联User的字段,配置如下:
@Entity public class Credential extends BaseModel { // 更多字段 @ManyToOne(targetEntity = User.class, cascade = CascadeType.ALL) @JoinTable(name = "user_cred", joinColumns = @JoinColumn(name = "cred_id", updatable = false, insertable = true), inverseJoinColumns = @JoinColumn(name = "user_id", updatable = false, insertable = true)) @JsonIgnore private User user; // setters getters }
同时User实体类包含关联Key的字段:
@Entity @Table(name = "users") public class User extends BaseModel { // 更多字段 @OneToOne(targetEntity = AESKey.class, cascade = CascadeType.ALL) @JoinColumn(name = "password_id", updatable = false, insertable = true) private Key password; // getter setter }
我已在Credential的User字段和User的Key字段上设置了updatable=false,但保存Credential对象时,Key对象仍会被更新,执行的SQL包含update key语句。尝试移除cascade = CascadeType.ALL后结果依旧。
我不想在Key类的字段上添加updatable=false,因为这会禁止直接更新Key对象,仅希望阻止通过保存Credential产生的间接更新,请问如何解决?
核心原因
updatable=false仅作用于实体关联的外键列,不会阻止Hibernate对关联实体本身的脏检查自动更新。当保存Credential时,若User及其关联的Key处于持久化上下文范围内,Hibernate会自动检测实体状态变化(哪怕没有实际修改),进而生成UPDATE语句。
可行解决方法
使用仅含ID的代理对象关联User
保存Credential时,不要传入完整的User实体,而是通过entityManager.getReference(User.class, userId)获取一个仅包含ID的代理对象。Hibernate只会处理外键关联,不会加载整个User及关联的Key,自然不会触发对Key的更新。从持久化上下文分离User/Key
在保存Credential前,调用entityManager.detach(user)将User及其关联的Key移出持久化上下文。这样Hibernate不再跟踪它们的状态变化,也就不会生成更新语句。注意:如果之后还需要操作该User,需要重新调用entityManager.merge(user)将其纳入上下文。修改级联策略为必要操作
将Credential中@ManyToOne的级联策略从CascadeType.ALL改为仅需要的类型,比如CascadeType.PERSIST(仅级联保存)或CascadeType.MERGE(仅级联合并,按需选择)。避免级联不必要的更新操作,结合代理对象使用效果更佳。以只读模式加载User
加载User时使用只读锁:entityManager.find(User.class, userId, LockModeType.READ),或者临时设置@org.hibernate.annotations.Entity(readOnly = true)。Hibernate对只读实体不会执行脏检查,也就不会触发关联Key的更新。
内容的提问来源于stack exchange,提问作者Seema Sharma

