2013-08-23 270 views
2

我在数据库中有两个表,A和B.表B有一个由两个字段组成的ID。其中之一是A的外键。A的Id由插入序列自动生成。JPA:持久实体与复合主键

A: 
    ID (PK) 
    (*other fields*) 

B: 
    SOME_FIELD (PK) 
    A_ID (PK, FK to A) 

我已映射在JPA(休眠)以下JPA specification两个表,在这种方式:

@Entity 
@Table(name = "A") 
public class A implements Serializable { 
    @Id 
    @SequenceGenerator(name = "A_SEQ", sequenceName = "A_SEQ") 
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "A_SEQ") 
    @Column(name = "ID") 
    private Long id; 
    (...) 
} 

@Entity 
@Table(name = "B") 
public class B implements Serializable { 
    @EmbeddedId 
    @AttributeOverride(name = "someField", column = @Column(name = SOME_FIELD)) 
    private BPK pk; 

    @MapsId("aId") 
    @ManyToOne 
    @JoinColumn(name = "A_ID") 
    private A a; 
    (...) 
} 

@Embeddable 
public class BPK implements Serializable { 
    private Long aId; 
    private String someField; 

    @Override 
    public boolean equals(Object o) { 
     (...) 
    } 

    @Override 
    public boolean hashCode() { 
     (...) 
    } 

    (...) 
} 

的问题是,当我试图保存B对象调用entityManager.persist(二),其中BA被设置为一个对象,它已经存在于数据库中,我得到一个异常:

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: package.name.A 

我不知道为什么会这样。我试图保存类B的对象,而不是A.我的实体类是否错误?或者,也许我不应该在这里坚持下去?

+0

您需要在实体类中实现'Serializable'。 – iNan

+0

我执行此操作。我没有复制“实现”部分,因为我实现了更多的接口,并且不想在我的代码中包含不必要的部分。用“implements Serializable”更新了这个问题。 – martaj

回答

3

可能是实体A不再被实体经理持有。你有没有试过用B.的“新鲜”实例来设置B.a?

b.setA(get(b.a)); 
entityManager.persist(b); 

get(b.a)方法可不管你平时使用从您的数据源如发现实体A entityManager.getReference(A.class, a.id);

+0

谢谢,它有帮助。但我不明白为什么没有这种“更新”就无法运作。对于其他实体,如果存在外键,但未包含在复合主键中,则只需调用entityManager.persist即可使用。在这种情况下,外键是主键的一部分吗? – martaj

+0

复合主键是一个奇怪的野兽 - 我过去曾与他们斗争过。说实话,正因为如此,我倾向于坚持主键的简单标识,然后将组合作为外键。 – KidTempo

+0

我认为这与你创建两个实体(B和BPK)和A是这两个实体的一部分有关。由于A(作为主键的一部分)对持久性至关重要,因此JPA坚持认为它是“新鲜的”。 – KidTempo