Java EntityManager.merge()未持久化相关实体
我正在使用eclipselink进行基本的JPA设置。我有一个实体来管理一个项目的库存量。我希望跟踪这些项目的库存历史记录,因此我希望对金额的每次更新也创建一个新的历史记录条目 问题是,当我在Java EntityManager.merge()未持久化相关实体,java,jpa,eclipselink,Java,Jpa,Eclipselink,我正在使用eclipselink进行基本的JPA设置。我有一个实体来管理一个项目的库存量。我希望跟踪这些项目的库存历史记录,因此我希望对金额的每次更新也创建一个新的历史记录条目 问题是,当我在@PreUpdate方法中将一个新的历史实体添加到历史列表中时,它将不会与Wine实体一起持久化。在调用merge()之前或在@PrePersist方法中手动添加历史记录实体,效果与预期相同 以下是我的代码: Wine.java @Entity public class Wine implements Se
@PreUpdate
方法中将一个新的历史实体添加到历史列表中时,它将不会与Wine实体一起持久化。在调用merge()
之前或在@PrePersist
方法中手动添加历史记录实体,效果与预期相同
以下是我的代码:
Wine.java
@Entity
public class Wine implements Serializable {
@Id
@GeneratedValue
private long id;
@Transient
private int oldAmount;
@Column
private int amount;
@OneToMany(mappedBy = "wine", cascade = CascadeType.ALL)
private List<AmountHistory> history;
//getters & setters
@PostLoad
@PostUpdate
@PostPersist
private void onLoad() {
oldAmount = amount;
}
@PrePersist
private void onPersist() {
final History history = new History();
history.setOldAmount(0);
history.setNewAmount(amount);
history.setWine(this);
this.history = new ArrayList<>();
this.history.add(history);
}
@PreUpdate
private void onUpdate() {
if (oldAmount != amount) {
final History history = new History();
history.setOldAmount(oldAmount);
history.setNewAmount(amount);
history.setWine(this);
this.history.add(history);
}
}
}
@Entity
public class History implements Serializable {
@Id
@GeneratedValue
private long id;
@Column
private int oldAmount;
@Column
private int newAmount;
@ManyToOne(cascade = CascadeType.ALL)
private Wine wine;
//getters & setters
}
我目前有一个解决办法,在setter方法中为amount添加历史记录条目,但我不相信这个解决方案,因为它在未调用merge()
时也会创建历史记录实体
我需要做什么来解决这个问题?或者有人能解释为什么会发生这种情况吗?您需要在历史实体/表中添加一个外键列,以创建两个表之间的关系,如下所示
@Entity
public class History implements Serializable {
@Id
@GeneratedValue
private long id;
@Column(name="WINE_ID", updateable=false, insertable=false)
private long wineId;
@Column
private int oldAmount;
@Column
private int newAmount;
@ManyToOne(cascade = CascadeType.ALL)
@JoinColumn(name="WINE_ID", referencedColumnName="ID")
private Wine wine;
//getters & setters
}
根据JPA规范第3.5节: “一般来说,可移植应用程序的生命周期方法不应调用EntityManager 或查询操作,访问其他实体实例,或修改 相同的持久性上下文。[43]生命周期回调方法可能会修改非关系 调用它的实体的状态。” 因此,您不应该试图在preUpdate回调中修改您的关系。在这种情况下,它不能像您所期望的那样工作,因为预更新发生在合并已经发生并在关系上级联之后-JPA不提供一种方法来触发合并再次发生而不手动调用它
解决方案是使用您的访问器方法来跟踪更改,因为您已经这样做了。除了跟踪oldAmount,还要添加一个新的历史记录实例。这样,您的合并将拾取关系以及更改的金额 外键丢失。所有者表(历史记录)上应该有一个外键列,以便在两个表之间创建关系。合并可以同时管理持久化和更新。如果它是一个临时实例,那么它将持久化该实体并返回托管实例,如果分离,那么它将更新并返回更新的实例。因此,可能您正在临时实例上调用merge,因此只调用prepersist处理程序。Eclipselink已经正确管理了表之间的关系,因为它在正常情况下可以很好地持久化/更新相关实体。当我在@PreUpdate方法中添加历史记录时,它就是不起作用。我理解。我认为您的Wine实体没有随历史一起更新/持久化的原因是,使用
@JoinColumn
在历史中指定了无外键关系。如果不指定联接关系,默认情况下,任何JPA实现都将使用主键,而主键可能并非始终正确。我建议您尝试使用回答中提到的@JoinColumn
指定一次外键关系。我尝试了,但没有成功。我应该澄清一下,我正在更新/保留一款葡萄酒,并期望它能与历史接轨。