2010-02-03 154 views
6

我在那里搜索,没有找到任何相似的主题,所以我发布了一个新的问题。hibernate复合主键包含一个复合外键,如何映射这个

我正在Hibernate上使用现有的数据库。我们不允许更改表格结构和数据。应用程序正在从数据库中读取数据,并根据某种逻辑迁移到另一个数据存储区。

现在的问题是关于复合PK映射。例如

表A具有复合PK。

Table A 
-------- 
a1 (pk) 
a2 (pk) 
a3 (pk) 
a4 (pk) 
foo 
bar 
======== 

表B也有一个复合PK,这个复合PK的一部分是A的PK,这里也是作为FK工作的。

Table B 
-------- 
a1 (fk,pk) 
a2 (fk,pk) 
a3 (fk,pk) 
a4 (fk,pk) 
b1 (pk) 
b2 (pk) 
b3 (pk) 
foo 
bar 
======== 

我尝试了几种方法,它们都不起作用。任何人都可以告诉一个可用的Hibernate映射解决方案吗更好的注释风格。

回答

9

在B的pk Class中将A的实体对象设置为@ManyToOne。

所以,如果你有

Class A 
Class APK - A's Primary Key 

Class B 
Class BPK - B's primary Key. 

BPK将包含A作为属性

@Embeddable 
public class BPK implements serializable { 
    .... 
    private A a; 

    @ManyToOne(fetch=FetchType.EAGER) 
    @JoinColumns ({ 
    @JoinColumn(name="...", referencedColumnName = "..."), 
    @JoinColumn(name="...", referencedColumnName = "..."), 
    ... 
    }) 
    public getA() { 
    return this.a; 
    } 
} 

documentation

@Embeddable继承 其所属的实体,除非的接入类型休眠使用3210特定注释@AccessType为 。复合外键(如果不是 使用默认敏感值) 在使用 @JoinColumns元素(基本上是@JoinColumn数组)的关联上定义。它 被认为是一个很好的做法 显式引用列名称 。否则,休眠将 假设您使用与 声明中主键 列相同的顺序。

+0

@Vinodh Ramasubramania嗨,使用实体作为主键不是一个好主意。 Hibernate Team推荐。您不能在用作主键的实体中限制HQL或条件中的查询结果。因此,有很多方法可以避免使用它。 – 2010-02-03 19:57:55

+0

但我没有使用实体作为PK。我使用它(A)与B的PK中的连接条件建立ManyToOne关系。在您的解决方案中,我没有看到B(BPK)的复合PK是如何用A(APK)的复合PK定义为它的一个子集。 – 2010-02-03 20:29:37

+0

您的BPK课程中的oneToMany可以帮助我。我没有想到这个方向。我一直在想如何引用BPK中的另一个APK类。而且不知道如何设置注释。 为了方便使用a.getBset(),我使用A类中的joincolumns建模了另一个One2Many。例如私人套装 bSet ;.在TypeB类中,也是manyToOne,指向TypeA。当然可以插入,updatable = false是需要的。 谢谢你们。 – Kent 2010-02-04 12:44:17

3

如果复合主键有只有代理键,使用@EmbeddableId

@Embeddable 
public class CompoundIdA implements Serializable { 

    private Integer field0; 
    private Integer field1; 
    private Integer field2; 
    private Integer field3; 

    @Column(name="FIELD_0") 
    public Integer getField0() { 
     return this.field0; 
    } 

    @Column(name="FIELD_1") 
    public Integer getField1() { 
     return this.field1; 
    } 

    @Column(name="FIELD_2") 
    public Integer getField2() { 
     return this.field2; 
    } 

    @Column(name="FIELD_3") 
    public Integer getField3() { 
     return this.field3; 
    } 

    public boolean equals(Object o) { 
     if(o == null) 
      return false; 

     if(!(o instanceof CompoundIdA)) 
      return false; 

     final CompoundIdA other = (CompoundIdA) o; 
     if(!(getField0().equals(other.getField0())) 
      return false; 

     if(!(getField1().equals(other.getField1())) 
      return false; 

     if(!(getField2().equals(other.getField2())) 
      return false; 

     if(!(getField2().equals(other.getField2())) 
      return false; 

     return true; 
    } 

    // hashcode impl 

} 

在ClassA的,我们有

@Entity 
public class ClassA { 

    private CompoundIdA compoundIdA; 

    @EmbeddedId 
    public CompoundIdA getCompoundIdA() { 
     return this.CompoundIdA; 
    } 

} 

如果复合主键有自然和代理键,再次使用@EmbeddableId

// Let's suppose field0 and field1 are both natural keys 
@Entity 
public class ClassA { 

    private CompoundIdA compoundIdA; 

    private Integer field0; 
    private Integer field1; 

    @EmbeddedId 
    public CompoundIdA getCompoundIdA() { 
     return this.CompoundIdA; 
    } 

    @Column(name="FIELD_0", insertable=false, updateable=false) 
    public Integer getField0() { 
     return this.field0; 
    } 

    @Column(name="FIELD_1", insertable=false, updateable=false) 
    public Integer getField1() { 
     return this.field1; 
    } 

} 

注意您必须设置insertable = false和updateable = false,因为多个属性共享同一列。否则,Hibernate会抱怨一些错误。

如果复合主键有只有自然键,如果你要定义一个@ManyToOne使用@IdClass

@Entity 
@IdClass(CompoundIdA.class) 
public class ClassA { 

    private Integer field0; 
    private Integer field1; 
    private Integer field2; 
    private Integer field3; 

    @Id 
    @Column(name="FIELD_0") 
    public Integer getField0() { 
     return this.field0; 
    } 

    @Id 
    @Column(name="FIELD_1") 
    public Integer getField1() { 
     return this.field1; 
    } 

    @Id 
    @Column(name="FIELD_2") 
    public Integer getField2() { 
     return this.field2; 
    } 

    @Id 
    @Column(name="FIELD_3") 
    public Integer getField3() { 
     return this.field3; 
    } 

} 

在ClassB的,你可以使用相同的方法如上图所示,财产,你必须设置插入=虚假和更新=假如下

@Entity 
public class ClassB { 

    private ClassA classA; 

    @ManyToOne 
    @JoinColumns ({ 
     @JoinColumn(name="FIELD_0", referencedColumnName="FIELD_0", insertable=false, updateable=false), 
     @JoinColumn(name="FIELD_1", referencedColumnName="FIELD_1", insertable=false, updateable=false), 
     @JoinColumn(name="FIELD_2", referencedColumnName="FIELD_2", insertable=false, updateable=false), 
     @JoinColumn(name="FIELD_3", referencedColumnName="FIELD_3", insertable=false, updateable=false) 
    }) 
    public ClassA getClassA() { 
     return this.classA; 
    } 

} 

问候,

+0

我真的没有看到你的答案和我的很大区别。除了你已经使用了替代注解来描述我已经列出的相同的东西。 – 2010-02-03 19:38:12

+0

我该如何写同样的ClassB.hbm.xml? – knocker 2013-01-03 06:37:58