【问题标题】:Java/Hibernate/JPA: cannot persist with compound key -> transient objectJava/Hibernate/JPA:不能使用复合键 -> 瞬态对象持久化
【发布时间】:2013-10-27 19:56:59
【问题描述】:

我的问题是我无法保存我的实体,因为它包含另一个实体,该实体由一个键映射,该键也是该表主键的一部分。该表如下所示:

table C:

+-----+------+
| id_A | id_B |
+-----+------+

..其中 idA 是表 A 的主键,EntityA,idB 是表 B 的主键,EntityB

所以它基本上是一个 n 到 m 的关系。这是我用于表 C 的实体:

@Entity
public class EntityC {

    private long idA;
    private EntityB b;

    @Id
    @Column(name = "id_A")
    public long getIdA() {
        return idA;
    }

    @Id
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_B")
    public EntityB getB() {
        return b;
    }

    ...setters are here...

}

请注意,id_A 按原样(id)映射,而id_B 映射为其对象表示,EntityB。这就是我想要做的:

EntityC c = new EntityC();
c.setIdA(123);
c.setB(new EntityB());

em.persist(c);
tx.commit();
em.close();

我想坚持EntityB,前提是我可以坚持EntityC

tx.commit() 我得到这个例外:org.hibernate.TransientObjectException: object references an unsaved transient instance

我想这是因为部分主键 id_B 没有保存。但是我将级联设置为所有,所以应该没有问题!

为什么这不起作用?


编辑:

当我这样做时:

em.persist(c.getB());
em.persist(c);

它有效。但是 Hibernate/JPA 不能自动做到这一点吗?我认为这就是级联的好处。


EDIT2:

添加了一个 embeddedId 而不是 id_A 和 id_B:

@Embeddable
public class EntityCID implements Serializable {

public long idA;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "id_B", referencedColumnName = "id")
public EntryB b;

}

EntityC 现在看起来像:

@Entity
public class EntityC implements Serializable {

    private EntityCID id;
    ...

    @EmbeddedId
    public void getId() {
        return id;
    }

}

但是如果我在em.persist(c) 之前没有em.persist(c.getId().b);,我仍然会得到瞬态对象异常。坚持这一点,虽然它很丑。

@Trein:它不是双向的。实体B代码:

@Entity
public class EntityB implements Serializable {
    public long id;
    public String text;
}

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    如果你仔细想想,你所看到的完全有道理。

    EntityC 是关系 CB 的“拥有方”:它定义了 JoinColumn,EntityB 具有“mappedBy”属性。

    所以在保存 C 时,事件的顺序通常是:

    • 插入 C/更新 C
    • 插入 B/更新 B

    现在在您的情况下,这会导致问题,因为显然 C 只有在 B 首先被持久化的情况下才能被保存。

    就您上面的陈述而言:我想坚持“只有在我可以坚持 EntityC 的情况下才能坚持 EntityB”。怎么会是这样?

    JPA 有一个“派生标识符”的概念,我不太熟悉,但在 Pro JPA 一书中将其定义为在以下情况下发生:

    当一个实体中的标识符包含另一个实体的外键时 实体,我们称之为派生标识符。因为实体包含 派生标识符依赖于另一个实体的身份, 我们称第一个为依赖实体。它依赖的实体 on 是多对一或一对一关系的目标 依赖实体,称为父实体

    现在,尽管最初的建议是您定义了两个 @Id 属性并且这是错误的,但似乎在 1-2-m 上有一个额外的 @Id 实际上在 JPA 2 中对于这种情况是有效的。

    这本书提供了多种处理派生标识符的方法,但是下面给出的一个示例看起来与您的情况非常相似。因此,您可能需要进一步调查 @MapsId 属性。

    @Entity
    public class Project {
    
    @EmbeddedId private ProjectId id;
    @MapsId("dept")
    @ManyToOne
    @JoinColumns({
    @JoinColumn(name="DEPT_NUM", referencedColumnName="NUM"),
    @JoinColumn(name="DEPT_CTRY", referencedColumnName="CTRY")})
    private Department department;
    // ...
    }
    
    @Embeddable
    public class ProjectId implements Serializable {
    
    @Column(name="P_NAME")
    private String name;
    @Embedded
    private DeptId dept;
    // ...
    }
    

    进一步了解:

    How do I properly cascade save a one-to-one, bidirectional relationship on primary key in Hibernate 3.6

    【讨论】:

      【解决方案2】:

      是双向关系吗?我建议您删除 @Id getB() 并执行修改:

      @OneToOne(cascade = CascadeType.ALL, mappedBy = "id_B")
      @PrimaryKeyJoinColumn(name = "id_B")
      public EntityB getB() {
          return b;
      }
      

      您的实体类必须只有一个用@Id 注释的属性。通常当你需要这个时,你会创建一个类来存储这两个属性,这将作为一个 Id 类。

      【讨论】:

      • 如果我这样做,我会从休眠中得到一个错误:NonUniqueObjectException。我想这是因为 hibernate 将 id_A 作为唯一 id 并且不允许持久化具有相同 id 的多个对象。
      • 实际上,如果您收到此错误,那是因为 Hibernate 抱怨当前的Session 已经包含一个与您要保存的新对象具有相同 ID 的对象。可以发一下EntityB的内容吗?
      • 我更正了自己:再次尝试并得到“javax.persistence.EntityExistsException:具有相同标识符值的不同对象已与会话关联”异常。我想一次保存多个具有相同 id_A 的实体。
      • 好吧,如果你必须修改一个已经被持久化的对象,你必须先从数据库中检索它。您不能只创建具有相同 ID 的另一个实例,对其进行更改,然后它们会保留它。这就是你在做的吗?
      • 我不想修改现有对象,我想创建两个新对象。顺便说一句,如果我在坚持c 之前手动坚持b(添加了一个例子)。但这真的是要走的路吗?
      【解决方案3】:

      您不能传递 new Entity() 以供参考。因为它里面没有任何值(甚至是主键)。那么hibernate如何将它作为外键插入到表中。如果没有保存,级联将保存您的父对象,无需为所有人​​调用保存方法。但是当你传递新对象时它不会这样做。

      【讨论】:

      • 但是主键应该在持久化时自动生成?没有儿童级联吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-06
      • 2011-06-14
      • 2017-04-09
      • 1970-01-01
      • 2013-11-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多