现在的位置: 首页 > 综合 > 正文

JPA Primary Keys through ManyToOne Relationships problem

2013年06月11日 ⁄ 综合 ⁄ 共 3277字 ⁄ 字号 评论关闭

A common model is to have a dependent object share the primary key of its parent. In the case of a OneToOne the child's primary key is the same as the parent, and in the case of a ManyToOne the child's primary key is composed of the parent's primary key and another locally unique field.

Unfortunately JPA does not handle this model well, and things become complicated, so to make your life a little easier you may consider defining a generated unique id for the child. It would be simple if JPA allowed the @Id annotation on a OneToOne or ManyToOne mapping, but it does not. JPA requires that all @Id mappings be Basic mappings, so if your Id comes from a foreign key column through a OneToOne or ManyToOne mapping, you must also define a Basic @Id mapping for the foreign key column. The reason for this is in part that the Id must be a simple object for identity and caching purposes, and for use in the IdClass or the EntityManager find() API.

Because you now have two mappings for the same foreign key column you must define which one will be written to the database (it must be the Basic one), so the OneToOne or ManyToOne foreign key must be defined to be read-only. This is done through setting the JoinColumn attributes insertable and updatable to false, or by using the @PrimaryKeyJoinColumn instead of the @JoinColumn.

A side effect of having two mappings for the same column is that you now have to keep the two in synch. This is typically done through having the set method for the OneToOne attribute also set the Basic attribute value to the target object's id. This can become very complicated if the target object's primary key is a GeneratedValue, in this case you must ensure that the target object's id has been assigned before relating the two objects.

Some times I think that JPA primary keys would be much simpler if they were just defined on the entity using a collection of Columns instead of mixing them up with the attribute mapping. This would leave you free to map the primary key field in any manner you desired. A generic List could be used to pass the primary key to find() methods, and it would be the JPA provider's responsibility for hashing and comparing the primary key correctly instead of the user's IdClass. But perhaps for simple singleton primary key models the JPA model is more straight forward.

TopLink / EclipseLink : Allow the primary key to be specified as a list of columns instead of using Id mappings. This allows OneToOne and ManyToOne mapping foreign keys to be used as the primary key without requiring a duplicate mapping. It also allows the primary key to be defined through any other mapping type. This is set through using a DescriptorCustomizer and the ClassDescriptor addPrimaryKeyFieldName API.
Hibernate / Open JPA: Allows the @Id annotation to be used on a OneToOne or ManyToOne mapping.

Example OneToOne id annotation

...
@Entity
public class Address {
    @Id
    @Column(name="OWNER_ID")
    private long ownerId;
 
    @OneToOne
    @PrimaryKeyJoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
 
    public void setOwner(Employee owner) {
        this.owner = owner;
        this.ownerId = owner.getId();
    }
    ...
}

Example OneToOne id XML

<entity name="Address" class="org.acme.Address" access="FIELD">
    <id name="ownerId">
        <column name="OWNER_ID"/>
    </id>
    <one-to-one name="owner">
        <primary-key-join-column name="OWNER_ID" referencedColumnName="EMP_ID"/>
    </one-to-one>
<entity/>

 

Example ManyToOne id annotation

...
@Entity
@IdClass(PhonePK.class)
public class Phone {
    @Id
    @Column(name="OWNER_ID")
    private long ownerId;
 
    @Id
    private String type;
 
    @ManyToOne
    @PrimaryKeyJoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...
 
    public void setOwner(Employee owner) {
        this.owner = owner;
        this.ownerId = owner.getId();
    }
    ...
}

From : 

Java Persistence/Identity and Sequencing

Java Persistence/ManyToOne

抱歉!评论已关闭.