【问题标题】:Limitation of JPA 1.0 using @IdClass with *nested* composite primary keys?JPA 1.0 使用带有 *nested* 复合主键的 @IdClass 的限制?
【发布时间】:2010-10-30 12:01:18
【问题描述】:

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

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

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

    ...
}

ProjectId 是:

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

谢谢

【问题讨论】:

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


    【解决方案1】:

    是的,这是 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 属性具有相同类型和名称的属性。

    【讨论】:

      【解决方案2】:

      问题在于 Hibernate 和 EclipseLink 都不知道如何将 Project 中的两个 redundant 属性 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;
          ...
      }
      

      【讨论】:

      • 这与可插入/可更新的东西无关。 ORM 如何知道 ProjectId 的 dept_x 映射到哪些属性?我不使用两者...休眠和EL。当 EntityManagerFactory 被实例化时,它们已经失败了。有点意思……
      • @Kawu:也许我没听懂你在说什么,但是当你重复上面的映射时,你必须在JoinColumn 中使用可插入/可更新的方式将关联设为只读。如果它不起作用,请显示允许重现的映射和一些说明问题的痕迹。
      • 让我澄清一下:JPA 1.0 不允许在关联上使用@Id,这就是为什么在 Project 中有两个冗余成员 deptNumber 和 deptCountry。由于这两个是 Departments 表中的一个 PK,因此 Project 的复合键类 ProjectId 有一个类型为 DeptId 的成员。但是 Hibernate 和 EclipseLink 都抛出了映射异常,因为它们无法将两个冗余的@Id 成员 deptNumber 和 deptCountry 映射到 DeptId 的属性。我想知道这是否是一般限制,或者是否有办法告诉 JPA 1.0 ORM 如何映射这些限制。 (JPA 1.0 要求匹配的字段名称)
      • @Kawu But Hibernate and EclipseLink both throw mapping exceptions, because they can't map the two redundant @Id members deptNumber and deptCountry to the DeptId's properties 这就是为什么您必须通过将可插入和可更新设置为 false 来将 ManyToOne 外键定义为只读......没有限制。你试过我的建议了吗?
      • 我在示例中省略了这些细节。在我的数据库模型中,insertable = false 和 updatable = false both 都存在。这是关于 ORM 无法find DeptId 中的相应属性,因为它们似乎只在查看 ProjectId。冗余字段和真实属性实际上是 两个 类,而不是一个:Project(冗余 deptNumber 和 deptCountry)-> ProjectId(DeptId ref)-> DeptId(声明编号和国家/地区)。我的数据库中有四次这个星座。它们都不起作用,即使 insertable = false 和 updatable = false ......它似乎并不相关。
      【解决方案3】:

      发布代码的问题是,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。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-11-10
        • 1970-01-01
        • 1970-01-01
        • 2020-06-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多