【发布时间】:2020-02-15 05:56:24
【问题描述】:
我在父实体和子实体之间有一个双向@OneToOne 关联,@MapsId 共享主键。
使用 Parent 对象,我可以添加一个 Child,删除它,然后添加一个新的 Child 对象:
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 对象而不删除(和刷新)旧的?
如果我从中间块中删除flush(),Hibernate 会抱怨标识符已经与会话相关联。我想我正在寻找一种方法来告诉 Hibernate 忽略旧的 Child 实体,允许新的具有相同的 id,并为新的 Child 运行 SQL 更新而不是插入。
我意识到我可以只更新当前的子字段,但我想知道是否可以进行完整的对象替换。
显示实体定义的完整代码在这里:
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
【问题讨论】:
-
我没有你的 Hibernate 版本,但我建议你试试
manager.remove(child);而不是child.parent = null;。也许您必须在刷新后添加manager.clear();。 -
感谢您的建议。如果我只使用
remove(child),即使我取消关联,我也会得到deleted object would be re-saved by cascade (remove deleted object from associations)。如果我也detach(child),Hibernate 会尝试插入并且我得到唯一约束违规。 -
你能显示已创建的 DDL(创建表,包括键、外键等)吗?
-
SQL 生成和最终表状态现在添加到问题中
-
我能想到的唯一其他方法是
detach来自经理的实体,使用createNativeQuery和refresh删除和插入子实体 - 但随后使用flush更容易,那为什么不坚持呢?顺便说一句:生成的 DDL 看起来与使用 Joined table strategy 的继承映射完全一样,并且一定不能删除 Child。