Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java EntityManager.merge()未持久化相关实体_Java_Jpa_Eclipselink - Fatal编程技术网

Java EntityManager.merge()未持久化相关实体

Java EntityManager.merge()未持久化相关实体,java,jpa,eclipselink,Java,Jpa,Eclipselink,我正在使用eclipselink进行基本的JPA设置。我有一个实体来管理一个项目的库存量。我希望跟踪这些项目的库存历史记录,因此我希望对金额的每次更新也创建一个新的历史记录条目 问题是,当我在@PreUpdate方法中将一个新的历史实体添加到历史列表中时,它将不会与Wine实体一起持久化。在调用merge()之前或在@PrePersist方法中手动添加历史记录实体,效果与预期相同 以下是我的代码: Wine.java @Entity public class Wine implements Se

我正在使用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);
        }
    }
}
@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
指定一次外键关系。我尝试了,但没有成功。我应该澄清一下,我正在更新/保留一款葡萄酒,并期望它能与历史接轨。