2

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

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

@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 属性):-> 它只是部门内的唯一名称

@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;

    ...
}

项目 ID 是:

public class ProjectId implements Serializable
{
    private String name;
    private DeptId dept;

    ...
}

问题在于 Hibernate 和 EclipseLink 都不知道如何将 Project 中的两个冗余属性 deptNum 和 deptCtry 映射到 DeptId 中的 dept 属性(或其中的属性)。-> MappingException 等。

我的问题是:

这是 JPA 1.0 的限制吗,具有组合键的表引用具有 @IdClass 实现的其他组合键通常不起作用,因为 JPA 实现根本不知道如何映射这些字段?

作为一种解决方法,您必须对这些类使用@EmbeddedId 或使用JPA 2.0 语法来用@Id 注释@XToX 关联。我只是想确保我对此的看法是正确的。

谢谢

4

3 回答 3

4

是的,这是 JPA 1.0 的限制,已在 JPA 2.0 中更正。在新的 JPA 2.0 中,您可以将 ID 注释放在您的部门关系上,并完全避免具有冗余的 deptCountry 和 deptNumber 属性,键类使用嵌套。在 JPA 1.0 中,只有基本映射可以标记为 ID 的一部分,需要冗余映射和一些代码来确保值/关系在持久化时正确放入缓存。由于冗余,如其他答案中所述,需要通过可插入/可更新=假将字段的映射之一标记为只读。这样做虽然意味着值不会合并到缓存中 - 因此除非从数据库刷新对象,否则不会反映更改(例如插入时,因为一旦对象 ID 存在就无法更改)。如果将 JoinColumns 标记为只读,则需要从引用的部门获取值,并在要持久化项目时手动将它们放入相应的基本 id 属性中。但是,您也可以将基本属性标记为只读。Eclipselink 无论如何不会有任何问题,并且会使用关联的部门实体正确设置字段值(只要在项目上调用 persist 之前设置)。请注意,当您在不同的上下文中回读项目时,可能会或可能不会填充基本属性 - 这将取决于实体是否从数据库中刷新。如果它们是只读的,它们不会被合并到共享缓存中,因为它们是只读的,不应该改变。所以它们可以被忽略,或者如果它们必须被填充,

可以通过使用 JPA2.0 @MapsId 重用相同的模型,它还将使用关系中的值为您维护基本映射。我看到的唯一好处是您不需要访问关系(可能导致惰性关系上不必要的连接或数据库访问)来获取外键/id 字段值。

至于 ZipArea EclipseLink 异常,它们是由于 ZipAreaId 具有 ZipId zip 属性而不是被展平。JPA 1.0 要求键类对实体中的每个@ID 属性具有相同类型和名称的属性。

于 2010-11-03T15:12:08.630 回答
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;
    ...
}
于 2010-10-31T11:26:45.907 回答
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。

于 2010-11-03T09:57:45.010 回答