简体   繁体   English

Java / Hibernate / JPA:无法使用复合键持久化->临时对象

[英]Java/Hibernate/JPA: cannot persist with compound key -> transient object

my problem is that I cannot save my entity because it contains another entity, mapped by a key that is also a part of this table's primary key. 我的问题是我无法保存我的实体,因为它包含另一个实体,该实体由一个也是该表主键一部分的键映射。 The table looks like this: 该表如下所示:

table C:

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

..where idA is the primary key of table A with EntityA and idB the primary key of table B with EntityB . ..其中idA是具有EntityA的表A的主键,而idB是具有EntityB的表B的主键。

so its basically a n-to-m relation. 因此基本上是n对m的关系。 This is the entity I'm using for table C: 这是我用于表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...

}

Please note that id_A is mapped as is (the id), while id_B is mapped as its object representation, EntityB . 请注意, id_A是按原样(id)映射的,而id_B被映射为其对象表示的EntityB This is what I want to do with it: 这就是我想要做的:

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

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

I want to persist EntityB ONLY IF I can persist EntityC . 我只想持久化EntityB我可以持久化EntityC

on tx.commit() I get this exception: org.hibernate.TransientObjectException: object references an unsaved transient instance tx.commit()我得到以下异常: org.hibernate.TransientObjectException: object references an unsaved transient instance

I suppose this happens because part of the primary key, id_B , is not saved. 我想发生这种情况是因为未保存主键id_B一部分。 But i set cascading to all so there should be no problem! 但是我将所有设置为层叠,所以应该没有问题!

Why is this not working? 为什么这不起作用?


EDIT: 编辑:

When I do this: 当我这样做时:

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

it works. 有用。 But can't Hibernate/JPA do that automatically? 但是Hibernate / JPA不能自动做到这一点吗? I thought that's what cascading is good for. 我认为这就是级联的好处。


EDIT2: EDIT2:

added an embeddedId instead of id_A and id_B: 添加了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 now looks like: EntityC现在看起来像:

@Entity
public class EntityC implements Serializable {

    private EntityCID id;
    ...

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

}

but I still get the transient object exception if I don't em.persist(c.getId().b); 但是如果我没有em.persist(c.getId().b);我仍然会收到瞬时对象异常em.persist(c.getId().b); before em.persist(c) . em.persist(c)之前。 Sticking to that, although it is ugly. 坚持下去,尽管很丑陋。

@Trein: it is not bidirectional. @Trein:不是双向的。 EntityB code: EntityB代码:

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

Is it a bidirectional relationship? 是双向关系吗? I would suggest you to remove @Id getB() and perform the modifications: 我建议您删除@Id getB()并执行修改:

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

Your entity class must have only one attribute annotated with @Id . 您的实体类必须只有一个带有@Id注释的属性。 Usually when you need this, you create a class that will store both properties and this will act as a Id Class. 通常,在需要时,您将创建一个将存储两个属性的类,并将其用作Id类。

You can not pass new Entity() for reference. 您不能将new Entity()传递给参考。 Because it won't have any values in it(even primary key). 因为它里面没有任何值(甚至主键)。 So how can hibernate will insert it as foreign key to the table. 因此,如何休眠将其作为表的外键插入。 And cascade will save your parent object if its not saved,no need to call save method for all. 如果没有保存,级联将保存您的父对象,无需全部调用save方法。 But when you passing new object it won't do. 但是,当您传递新对象时,它不会起作用。

If you think about it what you are seeing makes perfect sense. 如果您考虑一下,您所看到的将是很有意义的。

EntityC is is the 'owning side' of the relationship C<>B: it defines the JoinColumn and EntityB has the 'mappedBy' attribute. EntityC是关系C <> B的“拥有方”:它定义JoinColumn,而EntityB具有'mappedBy'属性。

So on saving C, order of events would normally be: 因此,在保存C时,事件的顺序通常为:

  • insert into C/update C 插入C / update C
  • insert into B/update B 插入B /更新B

Now in your case this causes issues as obviously C can only be saved if B has been persisted first. 现在,在您的情况下,这会导致出现问题,因为显然,只有先保留B才能保存C。

In terms of your statement above: I want to persist "EntityB ONLY IF I can persist EntityC." 根据上述声明,我要保留“只有在可以保留EntityC的情况下才保留EntityB”。 How can this ever be the case? 怎么会这样呢?

JPA has a concept of 'Derived Identifiers', which I am not overly familiar with however is defined in the book Pro JPA as occurring when: 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 它依赖的实体是从属实体的多对一或一对一关系的目标,称为父实体

Now, despite the original advice that you had two @Id attributes defined and this was wrong it would however appear that having an additional @Id on a 1-2-m is in fact valid in JPA 2 for precisely this case. 现在,尽管最初的建议是您定义了两个@Id属性,但这是错误的,但是在JPA 2中,实际上对于这种情况,实际上在1-2-m上具有一个额外的@Id是有效的。

The book gives a number of ways of dealing with Derived Identifiers however one example given below looks fairly similar to your case. 本书提供了多种处理派生标识符的方法,但是下面给出的一个例子看起来与您的案例非常相似。 So you may want to investigate further the @MapsId attribute. 因此,您可能想进一步研究@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;
// ...
}

See further: 进一步查看:

How do I properly cascade save a one-to-one, bidirectional relationship on primary key in Hibernate 3.6 如何在Hibernate 3.6的主键上正确地级联保存一对一的双向关系

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM