给出以下示例(部门 - 项目):
部门具有以下属性(复合主键):
@Entity
@IdClass(DeptId.class)
public class Department
{
@Id
@Column(name="number")
private Integer number;
@Id
@Column(name="country")
private String country;
@Column(name="name")
private String name;
@OneToMany(mappedBy="dept")
private Collection<Project> projects;
...
}
这里是PK类:
public class DeptId implements Serializable
{
private Integer number;
private String country;
...
}
项目与部门之间的关系是多对一的,即一个部门可以有很多项目。 Project类本身使用引用Department的复合键的复合键。重要提示:它仅与@IdClass的实现有关,而不是@EmbeddedId。
然后(有问题的)JPA 1.0 @IdClass实现必须看起来像那样(冗余deptNum和deptCtry属性): - &gt;它只是一个部门内的唯一名称
@Entity
@IdClass(ProjectId.class)
public class Project
{
@Id
@Column(name="dept_number")
private Integer deptNumber;
@Id
@Column(name="dept_country")
private String deptCountry;
@Id
@Column(name="name")
private String name;
@ManyToOne
@JoinColumns({
@JoinColumn(name="dept_number", referencedColumnName="number"),
@JoinColumn(name="dept_country", referencedColumnName="country")
})
private Department dept;
...
}
ProjectId是:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
问题在于,Hibernate和EclipseLink都不知道如何将Project中的两个冗余属性deptNum和deptCtry映射到DeptId中的dept属性(或其中的属性)。 - &GT; MappingException等。
我的问题是:
这是JPA 1.0的限制,带有复合键的表引用具有@IdClass实现的其他复合键通常无法正常工作,因为JPA实现根本无法知道如何映射这些字段?
作为一种解决方法,您必须对这些类使用@EmbeddedId或使用JPA 2.0语法来注释@Id的@XToX关联。我只是想确保我对此的看法是正确的。
由于
答案 0 :(得分:3)
是的,这是JPA 1.0的限制,在JPA 2.0中得到了纠正。在新的JPA 2.0中,您可以将ID注释放在dept关系上,并完全避免使用redundent deptCountry和deptNumber属性,并使用嵌套使用键类。在JPA 1.0中,只有基本映射可以标记为ID的一部分,需要redundent映射和一些代码以确保在持久化时正确地将值/关系放入缓存中。由于冗余,如其他答案中所述,字段的映射之一需要通过insertable / updatable = false标记为只读。这样做虽然意味着值没有合并到缓存中 - 因此,除非从数据库刷新对象,否则不会反映更改(例如插入时,因为除非存在,否则无法更改对象ID)。如果将JoinColumns标记为只读,则需要从引用的dept中获取值,并在要保留项目时手动将它们放入correspoinding basic id属性中。但是,您也可以将基本属性标记为只读。无论如何,Eclipselink都不会有任何问题,并且会使用关联的dept实体正确设置字段值(只要在项目上调用persist之前设置它)。请注意,当您在不同的上下文中回读项目时,可能会填充或不填充基本属性 - 这将取决于实体是否从数据库刷新。如果它们是只读的,则它们不会合并到共享缓存中,因为它们是只读的,不应该更改。因此可以忽略它们,或者如果必须填充它们,则刷新实体或在事件中从dept设置值。
使用JPA2.0 @MapsId可以重用这个相同的模型,它也将使用关系中的值来维护基本映射。我看到的唯一好处是你不需要访问关系(可能导致不必要的连接或对延迟关系的数据库访问)来获取外键/ id字段值。
至于ZipArea EclipseLink异常,它们归因于ZipAreaId具有ZipId zip属性而不是被展平。 JPA 1.0要求密钥类为实体中的每个@ID属性提供相同类型和名称的属性。
答案 1 :(得分:2)
问题在于,Hibernate和EclipseLink都不知道如何将Project中的两个冗余属性deptNum和deptCtry映射到DeptId中的dept属性
这就是您需要使用这种映射将ManyToOne
外键定义为只读的原因。这是通过将JoinColumn
属性insertable
和updatable
设置为false
来完成的。
请尝试以下方法:
@Entity
@IdClass(ProjectId.class)
public class Project
{
@Id
@Column(name="dept_number")
private Integer deptNumber;
@Id
@Column(name="dept_country")
private String deptCountry;
@Id
@Column(name="name")
private String name;
@ManyToOne
@JoinColumns({
@JoinColumn(name="dept_number", referencedColumnName="number", insertable=false, updatable=false),
@JoinColumn(name="dept_country", referencedColumnName="country", insertable=false, updatable=false)
})
private Department dept;
...
}
答案 2 :(得分:2)
发布代码的问题是,JPA 1.0实际上不允许嵌套复合主键类。此ProjectId无效:
public class ProjectId implements Serializable
{
private String name;
private DeptId dept;
...
}
DeptId必须被夷为平地,例如:
public class ProjectId implements Serializable
{
private Integer deptNumber;
private String deptCountry;
private String name;
...
}
我刚刚获得了EclipseLink版本,但Hibernate有问题。我想知道如何告诉Hibernate假定JPA 1.0。