EntityManager.merge()不持久保存相关实体

时间:2014-05-25 00:18:23

标签: java jpa eclipselink

我正在使用eclipselink进行基本的JPA设置。我有一个实体来管理物品的库存量。我想跟踪这些项目的库存历史记录,因此我希望每次更新金额也可以创建新的历史记录条目。

问题是当我在@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);
        }
    }
}

History.java

@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方法中添加历史记录条目来解决这个问题,但我不相信这个解决方案,因为它还会在未调用merge()时创建历史记录实体。

我需要做些什么来解决这个问题?或者有人可以解释为什么会这样吗?

2 个答案:

答案 0 :(得分:1)

您需要在History实体/表中添加一个外键列,以创建两个表之间的关系,如下所示。

@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
}

答案 1 :(得分:1)

从JPA规范的第3.5节开始: &#34;通常,可移植应用程序的生命周期方法不应调用EntityManager 或查询操作,访问其他实体实例或修改其中的关系 相同的持久性背景。[43]生命周期回调方法可以修改非关系 调用它的实体的状态。&#34;

因此,您不应该尝试修改preUpdate回调中的关系。在这种情况下,它无法正常工作,因为preUpdate发生在合并已经发生并在关系上级联之后 - JPA没有提供一种方法来触发合并再次发生而无需手动调用它。

解决方案是使用您的访问者方法跟踪已有的更改。而不是跟踪oldAmount,还添加新的历史记录实例。这样,您的合并将获取关系以及更改的金额。