22

我正在尝试为旧的数据库模式编写一个休眠适配器。此模式没有专用的 id 列,但使用大约其他三个列来连接数据。

在某些表上,我需要使用合并。到目前为止,这是我想出的:

关于定义:

  • 汽车可以具有由汽车用户或汽车用户组分配的元素。
  • 如果 FORIGN_ELEMENT 包含用户名,则定义将为 'u'
  • 如果 FORIGN_ELEMENT 包含一个组的名称,则定义将为 'g'
  • 这也意味着,一个表 (CAR_TO_ELEMENT) 被滥用来将汽车映射到元素,并将汽车组映射到元素。我定义了一个超类 CarElement 和子类 CarUserElement 和 CarGroupElement。
  • state 要么是“活动的”,要么是一个无趣的字符串
  • 我在其他地方设置了定义和状态,我们不需要担心这个。
  • 在连接表上使用 DEP_NR。如果为零,请使用 USR_DEP_NR。我COALESCE(NULLIF())在本机 SQL 中成功地做到了这一点,并希望在 Hibernate 中使用 Pojos 实现同样的目标。

好的,我们来看看代码:

@Entity
@Table(name="CAR")
public class Car extends TableEntry implements Serializable {
    @Id
    @Column(name="DEP_NR")
    private int depnr;

    @Id
    @Column(name="USER_NAME")
    @Type(type="TrimmedString")
    private String username;

    @ManyToOne(fetch = FetchType.EAGER, targetEntity=CarGroup.class)
    @JoinColumns(value={ 
            @JoinColumn(name="GROUP_NAME"),
            @JoinColumn(name="DEP_NR"),
            @JoinColumn(name="state"),
    })
    private CarGroup group;

    @OneToMany(fetch=FetchType.EAGER, targetEntity=CarUserElement.class, mappedBy="car")
    private Set<CarUserElement> elements;
}
@Entity
@Table(name="CAR_GROUP")
public class CarGroup extends TableEntry implements Serializable {
    @Id
    @Column(name="DEP_NR")
    private int depnr;

    @Id
    @Column(name="GROUP_NAME")
    @Type(type="TrimmedString")
    private String group;

    @ManyToOne(fetch = FetchType.EAGER, targetEntity=Car.class)
    @JoinColumns(value={ 
            @JoinColumn(name="GROUP_NAME"),
            @JoinColumn(name="DEP_NR"),
            @JoinColumn(name="state"),
    })
    private Set<Car> cars;

    @OneToMany(fetch=FetchType.EAGER, targetEntity=CarGroupElement.class, mappedBy="car")
    private Set<CarGroupElement> elements;
}
@MappedSuperclass
public class CarElement extends TableEntry {
    @Id
    @ManyToOne(fetch = FetchType.EAGER, targetEntity=Element.class)
    @JoinColumns(value={ 
            @JoinColumn(name="ELEMENT_NAME"),
            @JoinColumn(name="state"),
    })
    private Element element;
}
@Entity
@Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
    @Id
    @Column(name="DEFINITION")
    private char definition;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(value = {
        @JoinColumnOrFormula(formula=@JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
        @JoinColumnOrFormula(column=@JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="USER_NAME")),
        @JoinColumnOrFormula(column=@JoinColumn(name="STATE", referencedColumnName="STATE"))
    })
    private Car car;

}
@Entity
@Table(name="CAR_TO_ELEMENT")
public class CarGroupElement extends CarElement {
    @Id
    @Column(name="DEFINITION")
    private char definition;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(value = {
        @JoinColumnOrFormula(formula=@JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
        @JoinColumnOrFormula(column=@JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="GROUP_NAME")),
        @JoinColumnOrFormula(column=@JoinColumn(name="STATE", referencedColumnName="STATE"))
    })
    private Car car;

}

我尝试了所有可用的休眠版本(从 3.5.1 [第一个版本@JoinColumnsOrFormulas] 到 4.xx),但我总是收到此错误:

Exception in thread "main" java.lang.ClassCastException: org.hibernate.mapping.Formula cannot be cast to org.hibernate.mapping.Column
    at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:351)
    at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1338)
    at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:791)
    at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:719)
    at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:668)
    at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:66)
    at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1597)
    at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1355)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1737)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1788)

其他休眠用户似乎也有同样的问题:他们无法使用任何版本,请参阅此线程和其他 stackoverflow 问题: https ://forum.hibernate.org/viewtopic.php?f=1&t=1010559

为了更完整,这是我的 TrimmedString 类: https ://forum.hibernate.org/viewtopic.php?p=2191674&sid=049b85950db50a8bd145f9dac49a5f6e#p2191674

提前致谢!

PS:它适用于仅使用一个 DEP-NR-Column 连接这三个列(即仅使用 @JoinColumns 的 DEP_NRUSR_DEP_NR)。但我需要这个coalesce(nullif())

4

3 回答 3

4

我遇到了类似的问题,似乎问题在于您在@Id 中使用了@Formula。Hibernate 希望 Id 是可插入的,而公式是只读的。

在我的情况下,我能够通过自己创建各个列的 Id 属性并将连接的对象作为单独的属性来解决该问题。我不知道这是否适用于您的情况,因为您在公式中使用了两个不同的列,但如果是这样,您的代码可能类似于:

@Entity
@Table(name="CAR_TO_ELEMENT")
public class CarUserElement extends CarElement {
    @Id
    @Column(name="DEFINITION")
    private char definition;

    @Id
    @Column(name="DEP_NR")
    private Integer depNr;

    @Id
    @Column(name="USR_DEP_NR")
    private Integer usrDepNr;

    @Id
    @Column(name="FORIGN_ELEMENT")
    private String userName;

    @Id
    @Column(name="STATE")
    private String state;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumnsOrFormulas(value = {
        @JoinColumnOrFormula(formula=@JoinFormula(value="COALESCE(NULLIF(DEP_NR, 0), USR_DEP_NR)", referencedColumnName="DEP_NR")),
        @JoinColumnOrFormula(column=@JoinColumn(name="FORIGN_ELEMENT", referencedColumnName="USER_NAME", insertable = false, updatable = false)),
        @JoinColumnOrFormula(column=@JoinColumn(name="STATE", referencedColumnName="STATE", insertable = false, updatable = false))
    })
    private Car car;

}
于 2015-08-14T13:22:32.027 回答
1

连接公式在 Hibernate 中暂时是很脆弱的;我总是很难让它们正常工作。

经常帮助我的解决方法是创建暴露正确列的数据库视图(包括原始表中不存在的外键)。然后我使用分类 Hibernate/JPA 映射将实体映射到视图。

有时在使用此类实体时生成的 SQL 中存在冗余连接,但数据库在大多数情况下会优化此类查询,以便执行计划无论如何都是最优的。

另一种方法可能是使用@Subselects,这是某种 Hibernate 视图,但我希望它们的性能不如经典数据库视图。

于 2015-08-17T12:01:11.430 回答
0

我也遇到了演员表异常,而且我使用的是 Hibernate 5.x。

在 Hibernate 专门花时间解决这个问题之前,我发现虽然这个人的方法可能不是最干净的(他甚至逃避这个事实!),但它确实有效。

您只需将 @Column 映射(和 get/set 方法)添加到返回 null 的关联表对象中,并在填充关系数据时手动设置值。简单但有效!

在此处输入图像描述

于 2018-02-27T23:32:52.747 回答