使用具有3层结构的@IdClass在主键中使用复合键进行JPA 2 Hibernate映射

时间:2014-09-16 16:53:45

标签: java hibernate jpa java-ee-6

此问题非常类似于:JPA (Hibernate, EclipseLink) mapping: why doesn't this code work (chain of 2 relationships using JPA 2.0, @EmbeddedId composite PK-FK)?

实际上我的唯一(从我发现的有意义的)差异是我使用@IdClass并且我很可能无法切换到与休眠不同的提供商。

但无论如何这里是代码(删除不重要的部分):

PermissionContextType.java

@Entity
@IdClass(PermissionContextTypePk.class)
public class PermissionContextType{
   @Id    
   private String id;    

   @Id
   @JoinColumn (name = "PROJECT", referencedColumnName = "ID")
   @ManyToOne ()
   private Project project;

   public static class PermissionContextTypePk implements Serializable{
       public String project;
       public String id;
       // ... eq and hashCode here ...
   }

}

PermissionContext.java

@Entity
@IdClass(PermissionContextPk.class)
public class PermissionContext{
   @Id
   private String id;

   @Id
   @JoinColumns ({
            @JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
            @JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "ID")
        })
   @ManyToOne
   private PermissionContextType permissionContextType;

   public static class PermissionContextPk implements Serializable{
      public String id;
      public PermissionContextTypePk permissionContextType;
      // ... eq and hashCode here ...
   }
}

Permission.java

@Entity
@IdClass(PermissionPk.class)
public class Permission{

   @Id
   private String id;

   @Id
   @JoinColumns ({
            @JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT"),
            @JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE"),
            @JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID")
        })
   @ManyToOne
   private PermissionContext permissionContext;

   public static class PermissionPk implements Serializable{
      public String id;        
      public PermissionContextPk permissionContext;
      // ... eq and hashCode here ...
   }
}

我得到的是:

 org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a @MapsId: PermissionContext 
    Caused by: org.hibernate.AssertionFailure: org.hibernate.AssertionFailure: Unexpected nested component on the referenced entity when mapping a @MapsId: PermissionContext

有没有人知道这是一个hibernate错误我应该在他们的问题跟踪系统上发布(并祈祷我能够更新到给定的hibernate版本)或者我的绑定实体的方式是否有根本错误?

我已经使用EAP 6.1(4.2.0)上的hibernate实现以及wildfly(不知道哪一个)进行了检查。

2 个答案:

答案 0 :(得分:1)

好的,所以这是我到目前为止所发现的:

感谢我的朋友:https://hibernate.atlassian.net/browse/HHH-5764,这很可能就是这种行为的原因。

我找到了解决方法:

Permission.java:

@Entity
@IdClass(PermissionPk.class)
public class Permission{

   @Id
   private String id;

// for the next 3 fields there are no public acessors, so the public API of the class was not changed !
   @Id
   @Column(name = "PROJECT")
   private String projectId;

   @Id
   @Column(name = "PERMISSIONCONTEXTTYPE")
   private String permissionContextTypeId;

   @Id
   @Column(name = "PERMISSIONCONTEXT")
   private String permissionContextId;


   @JoinColumns ({
            @JoinColumn (name = "PROJECT", referencedColumnName = "PROJECT", updatable = false, insertable = false),
            @JoinColumn (name = "PERMISSIONCONTEXTTYPE", referencedColumnName = "PERMISSIONCONTEXTTYPE", updatable = false, insertable = false),
            @JoinColumn (name = "PERMISSIONCONTEXT", referencedColumnName = "ID", updatable = false, insertable = false)
        })
   @ManyToOne
   private PermissionContext permissionContext;

   public static class PermissionPk implements Serializable{      
// previously they where private as well, but removed public constructor for the sake of simplicity of the question - so no changes where necesary in public API of the class !
      private String id;        
      private String projectId;        
      private String permissionContextTypeId;
      private String permissionContextId;

   public PermissionPk () {}

    public PermissionPk (String aId, PermissionContextPk aPermissionContext) {
        this.id = aId;
        permissionContextId = aPermissionContext.id;
        permissionContextTypeId = aPermissionContext.permissionContextType.id;
        projectId = aPermissionContext.permissionContextType.project;
    }  
... eq and hashCode here ...
   }
}

这种解决方法的好处是它不会以任何方式更改类的公共API (唯一的变化是我需要在Perk中显示Pk的上下文和上下文类型的字段 - 它们之前只有一个公共构造函数的私有[但是再次简化了问题]),也没有改变jpql查询,同时解决方法是可扩展的(任何层数 - 只要每个偶数pk不包含另一个pk),所以如果错误将被解决,将很容易删除变通方法。

我仍然乐意接受对我的解决方法或问题本身的任何评论。

答案 1 :(得分:0)

今天我发现了另一种解决方法:) 您可以完全省略@IdClass并使用特定于hibernate的功能来动态创建复合键,因为它显然不受此错误的影响。 这里的缺点是:

  1. 它完全是Hibernate特有的,完全不属于JPA。
  2. 你不能做em.find(ClassName.class,new ClassPk(args ...)),因为根本没有ClassPk。
  3. 但是如果你可以使用除休眠之外的任何东西,你也可以使用没有这个bug的东西 - 所以可能1真的不是问题。并且你可能不需要为这个实体提供em.find(或者可以通过session或jpql查询来创建它)。