Java/hibernate/JPA : cannot persist with compound key -> transient object

标签 java hibernate jpa

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

table C:

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

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

所以它基本上是一个 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();

只有当我可以保留 EntityC 时,我才想保留 EntityB

tx.commit()上我得到这个异常:org.hibernate.TransientObjectException:对象引用未保存的 transient 实例

我认为发生这种情况是因为主键 id_B 的一部分未保存。但我设置了级联,所以应该没有问题!

为什么这不起作用?

<小时/>

编辑:

当我这样做时:

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

它有效。但是 Hibernate/JPA 不能自动执行此操作吗?我认为这就是级联的好处。

<小时/>

编辑2:

添加了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); ,我仍然会遇到 transient 对象异常。坚持下去,尽管它很丑。

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

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

最佳答案

如果你仔细想想,你所看到的一切就很有意义。

EntityC 是关系 C<>B 的“拥有方”:它定义 JoinColumn,EntityB 具有“mappedBy”属性。

因此,在保存 C 时,事件顺序通常是:

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

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

就您上面的陈述而言:我想坚持“仅当我可以坚持实体C时才坚持实体B”。怎么会这样呢?

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

When an identifier in one entity includes a foreign key to another entity, we call it a derived identifier. Because the entity containing the derived identifier depends upon another entity for its identity, we call the first the dependent entity. The entity that it depends upon is the target of a many-to-one or one-toone relationship from the dependent entity, and is called the parent entity

现在,尽管最初的建议是定义了两个 @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

关于Java/hibernate/JPA : cannot persist with compound key -> transient object,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19458708/

相关文章:

java - A* (A Star) 算法输出所有可能的解决方案

java - Dropwizard 嵌套事务

mysql - Java JPA - 持久连接表

java - 使用数据库jpa中现有和不存在的数据持久保存新数据时出错

spring - 如何在 Spring Data JPA 中使用带有分页的投影接口(interface)?

java - 得到 JdkVersion classNotFoundException

java - Spring云函数Cosumingrabbitmq队列-Dispatcher没有 channel 订阅者

java - 创建 .sh 文件并在 Linux 命令中修复它

hibernate - Hibernate3 Grails3插件

java - 如何在 Jboss 6 中动态更改数据源连接 url