JPA / Hibernate5:如何替换用@OneToOne和@MapsId映射的子对象?

时间:2019-10-18 09:00:18

标签: java hibernate jpa

我在父级和子级实体之间有一个双向@OneToOne关联,并带有@MapsId来共享主键。

对于父对象,我可以添加一个子对象,将其删除,然后添加一个新的子对象:

        Parent parent = new Parent();
        parent.name = "parent";
        Child child = new Child();
        child.name = "child";
        child.parent = parent;
        parent.child = child;
        manager.persist(parent);
        manager.flush();

        // Replace this block with ?
        parent.child = null;
        child.parent = null;
        manager.persist(parent);
        manager.flush();

        Child child2 = new Child();
        child2.name = "child2";
        child2.parent = parent;
        parent.child = child2;
        manager.persist(parent);

但是,有没有一种方法可以设置一个新的Child对象,而不删除(并刷新)旧的Child对象?

如果我从中间块中删除flush(),则Hibernate会抱怨该标识符已经与会话关联。我想我正在寻找一种方法来告诉Hibernate忽略旧的Child实体,允许新的实体具有相同的ID,并为新的Child运行SQL更新而不是插入。

我意识到我可以只更新当前的Child字段,但是我想知道是否可以进行完整的对象替换。

显示实体定义的完整代码在这里:

import javax.persistence.*;

public class OneToOneExample {

    @Entity(name = "Parent")
    public static class Parent {
        @Id
        @GeneratedValue
        Long id;

        @OneToOne(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
        Child child;

        String name;
    }

    @Entity(name = "Child")
    public static class Child {
        @Id
        Long id;

        @OneToOne
        @MapsId
        Parent parent;

        String name;
    }

    private static void runExample(EntityManager manager) {
        Parent parent = new Parent();
        parent.name = "parent";
        Child child = new Child();
        child.name = "child";
        child.parent = parent;
        parent.child = child;
        manager.persist(parent);
        manager.flush();

        // Replace this block with ?
        parent.child = null;
        child.parent = null;
        manager.persist(parent);
        manager.flush();

        Child child2 = new Child();
        child2.name = "child2";
        child2.parent = parent;
        parent.child = child2;
        manager.persist(parent);
    }

    public static void main(String[] args) {
        EntityManagerFactory factory = null;
        EntityManager manager = null;
        try {
            factory = Persistence.createEntityManagerFactory(OneToOneExample.class.getPackageName());
            manager = factory.createEntityManager();
            manager.getTransaction().begin();
            runExample(manager);
            manager.getTransaction().commit();
        } finally {
            if (manager != null) manager.close();
            if (factory != null) factory.close();
        }
    }
}

例外:

Exception in thread "main" javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [....OneToOneExample$Child#1]
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:733)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:695)
    at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:492)
    at ...

我正在使用Hibernate 5.4.6.Final,Hibernate-JPA2.1 1.0.2.Final,HSQLDB 2.5.0

更新:原始的Hibernate SQL生成和表数据:

Hibernate: drop table if exists Child CASCADE 
Hibernate: drop table if exists Parent CASCADE 
Hibernate: drop sequence hibernate_sequence if exists
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Child (name varchar(255), parent_id bigint not null, primary key (parent_id))
Hibernate: create table Parent (id bigint not null, name varchar(255), primary key (id))
Hibernate: alter table Child add constraint FKlh67j1n7x7gt59u0pbkwqh6o6 foreign key (parent_id) references Parent
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Parent (name, id) values (?, ?)
Hibernate: insert into Child (name, parent_id) values (?, ?)
Hibernate: delete from Child where parent_id=?
Hibernate: insert into Child (name, parent_id) values (?, ?)

SELECT * FROM CHILD
    NAME, PARENT_ID
    child2, 1
SELECT * FROM PARENT
    ID, NAME
    1, parent

0 个答案:

没有答案