使用@IdClass和*嵌套*复合主键限制JPA 1.0?

时间:2010-10-30 12:01:18

标签: java hibernate eclipselink nested composite-primary-key

给出以下示例(部门 - 项目):

部门具有以下属性(复合主键):

@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关联。我只是想确保我对此的看法是正确的。

由于

3 个答案:

答案 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属性insertableupdatable设置为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。

相关问题