Hibernate/JPA 注释中的多列连接

Posted

技术标签:

【中文标题】Hibernate/JPA 注释中的多列连接【英文标题】:Multi-Column Join in Hibernate/JPA Annotations 【发布时间】:2012-06-06 10:23:22 【问题描述】:

我有两个实体,我想通过多个列加入它们。这些列由两个实体共享的@Embeddable 对象共享。在下面的示例中,Foo 只能有一个Bar,但Bar 可以有多个Foos(其中AnEmbeddableObjectBar 的唯一键)。这是一个例子:

@Entity
@Table(name = "foo")
public class Foo 
    @Id
    @Column(name = "id")
    @GeneratedValue(generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1)
    private Long id;
    @Embedded
    private AnEmbeddableObject anEmbeddableObject;
    @ManyToOne(targetEntity = Bar.class, fetch = FetchType.LAZY)
    @JoinColumns( 
        @JoinColumn(name = "column_1", referencedColumnName = "column_1"),
        @JoinColumn(name = "column_2", referencedColumnName = "column_2"),
        @JoinColumn(name = "column_3", referencedColumnName = "column_3"),
        @JoinColumn(name = "column_4", referencedColumnName = "column_4")
    )
    private Bar bar;

    // ... rest of class

还有 Bar 类:

@Entity
@Table(name = "bar")
public class Bar 
    @Id
    @Column(name = "id")
    @GeneratedValue(generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1)
    private Long id;
    @Embedded
    private AnEmbeddableObject anEmbeddableObject;

    // ... rest of class

最后是AnEmbeddedObject 类:

@Embeddable
public class AnEmbeddedObject 
    @Column(name = "column_1")
    private Long column1;
    @Column(name = "column_2")
    private Long column2;
    @Column(name = "column_3")
    private Long column3;
    @Column(name = "column_4")
    private Long column4;

    // ... rest of class

显然架构的标准化很差,这是AnEmbeddedObject的字段在每个表中重复的限制。

我遇到的问题是当我尝试启动 Hibernate 时收到此错误:

org.hibernate.AnnotationException: referencedColumnNames(column_1, column_2, column_3, column_4) of Foo.bar referencing Bar not mapped to a single property

我尝试将 JoinColumns 标记为不可插入和不可更新,但没有成功。有没有办法用 Hibernate/JPA 注释来表达这一点?

【问题讨论】:

如果您从Foo 中删除嵌入文件会怎样? 【参考方案1】:

如果这不起作用,我就没有主意了。通过这种方式,您可以获得两个表中的 4 列(Bar 拥有它们,Foo 使用它们来引用 Bar)和两个实体中生成的 ID。 4 列的集合在 Bar 中必须是唯一的,因此多对一关系不会变成多对多。

@Embeddable
public class AnEmbeddedObject

    @Column(name = "column_1")
    private Long column1;
    @Column(name = "column_2")
    private Long column2;
    @Column(name = "column_3")
    private Long column3;
    @Column(name = "column_4")
    private Long column4;


@Entity
public class Foo

    @Id
    @Column(name = "id")
    @GeneratedValue(generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1)
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumns(
        @JoinColumn(name = "column_1", referencedColumnName = "column_1"),
        @JoinColumn(name = "column_2", referencedColumnName = "column_2"),
        @JoinColumn(name = "column_3", referencedColumnName = "column_3"),
        @JoinColumn(name = "column_4", referencedColumnName = "column_4")
    )
    private Bar bar;


@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = 
    "column_1",
    "column_2",
    "column_3",
    "column_4"
))
public class Bar

    @Id
    @Column(name = "id")
    @GeneratedValue(generator = "seqGen")
    @SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1)
    private Long id;
    @Embedded
    private AnEmbeddedObject anEmbeddedObject;

【讨论】:

这就是问题所在,“AnEmbeddedObject”必须存在于两个对象中。出于性能原因,生成的 id 更可取。 那么 4 列加上生成的键映射到 bar?这是非常非常糟糕的。如果您的解决方案是为Bar 生成代理键并且不要在Foo 外键中创建列,您必须自己设置Foo 中的列。如果我是对的,我可以调整我的答案。 ;-) @bowsie 我修改了我的答案,所以 FooBar 都有 4 列,BarFoo 中引用了它的 ID。 干杯。但这仍然不是我所追求的。出于性能原因,我不想查找 Bar 的主 ID,并且我希望 hibernate 应该允许我加入我想要的任何内容 - 如果我两次引用列,那么将 insertable 和 updatable 设置为 false 应该可以解决问题 - 就像使用单列连接。 @bowsie 我又调整了答案,也许这就是你要找的?【参考方案2】:

Hibernate 不会让您轻松地做您想做的事情。来自Hibernate documentation:

请注意,当对非主键列使用 referencedColumnName 时,关联的类必须是可序列化的。 另请注意,非主键列的 referencedColumnName 必须映射到具有单个列的属性(其他情况可能不起作用)。 (强调添加)

因此,如果您不愿意将AnEmbeddableObject 设为 Bar 的标识符,那么 Hibernate 不会懒惰地自动为您检索 Bar。当然,您仍然可以使用 HQL 编写加入 AnEmbeddableObject 的查询,但是如果您坚持对 Bar 使用多列非主键,您将失去自动获取和生命周期维护。

【讨论】:

我会说值得一试,理论上应该可以使用多个引用一个唯一约束的外键。如果不是OP的运气不好。 +1 这可能是答案。 @siebz0r,OP 已经拍摄并得到了几乎完全符合我对这种错误情况所期望的错误消息:“referencedColumnNames...未映射到单个属性”。它应该说“未映射到具有单列的属性”,但它是同一件事。 我猜游戏结束了 ;-) 感谢您的信息。与此同时,我一直在使用 HQL - 但很高兴了解这些限制。 +1【参考方案3】:

这对我有用。在我的情况下,必须根据 3 个不同的列连接 2 个表 foo 和 boo。请注意,在我的情况下,在 boo 中,3 个常用列不是主键

即基于 3 个不同列的一对一映射

@Entity
@Table(name = "foo")
public class foo implements Serializable

    @Column(name="foocol1")
    private String foocol1;
    //add getter setter
    @Column(name="foocol2")
    private String foocol2;
    //add getter setter
    @Column(name="foocol3")
    private String foocol3;
    //add getter setter
    private Boo boo;
    private int id;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "brsitem_id", updatable = false)
    public int getId()
    
        return this.id;
    
    public void setId(int id)
    
        this.id = id;
    
    @OneToOne
    @JoinColumns(
    
        @JoinColumn(updatable=false,insertable=false, name="foocol1", referencedColumnName="boocol1"),
        @JoinColumn(updatable=false,insertable=false, name="foocol2", referencedColumnName="boocol2"),
        @JoinColumn(updatable=false,insertable=false, name="foocol3", referencedColumnName="boocol3")
    
    )
    public Boo getBoo()
    
        return boo;
    
    public void setBoo(Boo boo)
    
        this.boo = boo;
    






@Entity
@Table(name = "boo")
public class Boo implements Serializable

    private int id;
    @Column(name="boocol1")
    private String boocol1;
    //add getter setter
    @Column(name="boocol2")
    private String boocol2;
    //add getter setter
    @Column(name="boocol3")
    private String boocol3;
    //add getter setter
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "item_id", updatable = false)
    public int getId()
    
        return id;
    
    public void setId(int id)
    
        this.id = id;
    

【讨论】:

在我的情况下,我想加入一列,但删除的字段应该为空,以便进行加入(我不想检索软删除的项目)如何实现?

以上是关于Hibernate/JPA 注释中的多列连接的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 hibernate/jpa 注释将一个类映射到不同的表

Hibernate/JPA 是不是考虑了瞬态修饰符(而不是注释)

Spring Security 3 身份验证与 Hibernate 3(JPA) 注释的集成

hibernate jpa将两个表与另一个表连接起来

Hibernate/JPA @OneToOne 返回空指针异常

Hibernate/JPA:如何强制隐式连接使用 LEFT OUTER JOINS