简体   繁体   English

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

[英]JPA 2 Hibernate mapping with composite key in primary key using @IdClass with 3 tier structure

This question is very similar to: JPA (Hibernate, EclipseLink) mapping: why doesn't this code work (chain of 2 relationships using JPA 2.0, @EmbeddedId composite PK-FK)? 这个问题非常类似于: JPA(Hibernate,EclipseLink)映射:为什么这段代码不起作用(使用JPA 2.0,@ EmbeddedId复合PK-FK的2个关系链)?

Actually my only (from meaningful that I spotted) difference is that I use @IdClass and that I most probably won't be able to switch to a different provider than hibernate. 实际上我的唯一(从我发现的有意义的)差异是我使用@IdClass并且我很可能无法切换到与hibernate不同的提供者。

but anyway here is the code (removed parts that where unimportant): 但无论如何这里是代码(删除不重要的部分):

PermissionContextType.java : 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 : 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 : 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 ...
   }
}

and what I get is: 而我得到的是:

 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

does anybody know if this is a hibernate bug and I should post it on their issue tracking system (and pray that I would be able to update to given hibernate version) or is there something fundamentally wrong with my way of binding the entities? 有没有人知道这是一个hibernate错误,我应该将它发布在他们的问题跟踪系统上(并祈祷我能够更新到给定的hibernate版本)或者我的绑定实体的方式是否存在根本错误?

I've checked it with the hibernate implementation on EAP 6.1 (4.2.0) as well as on wildfly (don't really know which one.) 我已经使用EAP 6.1(4.2.0)上的hibernate实现以及wildfly(不知道哪一个)进行了检查。

Ok, so this is what I found so far : 好的,所以这是我到目前为止所发现的:

Thanks fr my friend : https://hibernate.atlassian.net/browse/HHH-5764 which most probably is the reason for this behaviour. 谢谢我的朋友: https//hibernate.atlassian.net/browse/HHH-5764这很可能就是这种行为的原因。

And I found a workaround : 我发现了一个解决方法:

Permission.java: 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 ...
   }
}

The good thing about this workaround is that it does not change the public API of the class in any way (the only change was that I needed to make fields in Pk's of context and contexttype visible to the PermissionPk - they where private before with only a public constructor [but again simplified for the question]), nor did it change the jpql queries, and at the same time workaround is scalable (to any tier amount - as long as every even pk does not contain another pk), so if the bug will be resolved it will be easy to remove the workaround. 这个解决方法的好处是它不会以任何方式更改类的公共API(唯一的变化是我需要在Pk的上下文中创建字段,并且上下文类型对PermissionPk可见 - 它们之前是私有的,只有一个公共构造函数[但同样简化了问题]),也没有改变jpql查询,同时解决方法是可扩展的(任何层数 - 只要每个偶数pk不包含另一个pk),所以如果将解决错误,将很容易删除变通方法。

I would still gladly accept any comments on either my workaround or the question in itself. 我仍然乐意接受对我的解决方法或问题本身的任何评论。

Today I found another workaround :) You can omit @IdClass entirely and use hibernate specific ability to create composite keys on the fly as apparently it is not affected by this bug. 今天我找到了另一种解决方法:)你可以完全省略@IdClass并使用hibernate特定的能力来动态创建复合键,因为它显然不受此bug的影响。 The drawback here is that: 这里的缺点是:

  1. it is entirely Hibernate specific not covered by JPA at all. 它完全是Hibernate特有的,不是JPA所涵盖的。
  2. you cannot do em.find(ClassName.class,new ClassPk(args...)) as there is no ClassPk at all. 你不能做em.find(ClassName.class,new ClassPk(args ...)),因为根本没有ClassPk。

But if you could use anything else than hibernate you could just as well use something without this bug - so probably 1 is not a problem really. 但是,如果你可以使用除休眠之外的任何东西,你也可以使用没有这个bug的东西 - 所以可能1真的不是问题。 and there is a possibility that you don't really need the em.find for this entity (or can live with creating it thru session or jpql query). 并且你可能不需要为这个实体提供em.find(或者可以通过session或jpql查询来创建它)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM