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
Idmappings. This allowsOneToOneandManyToOnemapping 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 aDescriptorCustomizerand theClassDescriptoraddPrimaryKeyFieldNameAPI.
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();
}
...
}
本文探讨了Java Persistence API (JPA)中处理复合主键和外键作为主键的复杂场景,包括如何通过注解和XML配置实现OneToOne及ManyToOne关系映射,并保持数据同步。
1603

被折叠的 条评论
为什么被折叠?



