在上一篇文章中 ,我提到我选择通过其主键而不是类型来引用其他聚合。 在处理大型或复杂域模型时,我通常使用这种方法(也称为断开域模型)。 在这篇文章中,让我尝试进一步解释如何在JPA中完成它。 请注意,生成DDL脚本不会创建一个外键约束(不像所示的一个以前的帖子 )。
通过身份引用
在大多数JPA示例中,每个实体都引用另一个实体,或者被另一个实体引用。 这导致了对象模型,该对象模型允许从一个实体到任何其他实体的遍历。 这会导致不必要的遍历 (以及持久化操作的不必要的级联)。 这样,最好通过按ID(而不是按类型)引用其他实体来防止这种情况。
 下面的代码显示OrderItem如何通过其主键(而不是类型)引用Product实体。 
@Entity
public class Product {@Id private Long id;// ...
}@Entity
public class Order {// ...@OneToMany(mappedBy="order")private Collection<OrderItem> items;
}@Entity
public class OrderItem {// ...@ManyToOneprivate Order order;// @ManyToOne// private Product product;private Long productId;// ...
} 有几种获取关联的Product实体的方法。 一种方法是使用存储库查找具有ID的产品(具有findByIdIn(List<Long> ids)方法的ProductRepository )。 如之前的评论中所述,请注意不要以N + 1选择问题告终。 
 也可以使用自定义身份类型。 下面的示例使用ProductId 。 它是一个价值对象。 并且由于JPA,我们需要添加零参数构造函数。 
@Embeddable
public class ProductId {private Long id;public ProductId(long id) {this.id = id;}public long getValue() { return id; }// equals and hashCodeprotected ProductId() { /* as required by JPA */ }
}@Entity
public class Product {@EmbeddedId private ProductId id;// ...
}@Entity
public class Order { // ...@OneToMany(mappedBy="order")private Collection<OrderItem> items;
}@Entity
public class OrderItem {// ...@ManyToOneprivate Order order;// @ManyToOne// private Product product;@Embedded private ProductId productId;// ...
}但是,如果您将生成的值用于ID,则无法使用。 幸运的是,从JPA 2.0开始,围绕此有一些技巧,我将在下一部分中分享这些技巧。
生成的ID
 在JPA中,当使用非@Basic类型作为@Id ,我们不能再使用@GeneratedValue 。 但是,通过混合使用属性和字段访问,我们仍然可以使用生成的值和ProductId 。 
@Embeddable
@Access(AccessType.FIELD)
public class ProductId {...}@Entity
@Access(AccessType.FIELD)
public class Product {@Transient private ProductId id;public ProductId getId() { return id; }// ...private Long id_;@Id@GeneratedValue(strategy=...)@Access(AccessType.PROPERTY)protected Long getId_() { return id_; }protected void setId_(Long id_) {this.id_ = id_;this.id = new ProductId(this.id_);}
}@Entity
public class Order { // ...@OneToMany(mappedBy="order")private Collection<OrderItem> items;
}@Entity
public class OrderItem {// ...@ManyToOneprivate Order order;// @ManyToOne// private Product product;@Embedded private ProductId productId;// ...
} 诀窍包括将属性访问权限用于生成的ID值(同时保留其余访问权限)。 这将使JPA使用setter方法。 然后在其中初始化ProductId字段。 请注意, ProductId字段不会@Transient (标记为@Transient )。 
希望这可以帮助。
翻译自: https://www.javacodegeeks.com/2016/07/reference-identity-jpa.html