Hibernate和@JoinFormula:org.hibernate.mapping.Formula无法强制转换为org.hibernate.mapping.Column

时间:2013-10-22 13:48:20

标签: java sql hibernate

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

在某些表格中,我需要使用合并。这是我到目前为止所提出的:

关于定义:

  • 汽车可以包含由汽车用户或汽车用户分配的元素。
  • 如果FORIGN_ELEMENT包含用户名,则定义为“u”
  • 如果FORIGN_ELEMENT包含组名,则定义为'g'
  • 这也意味着,一个表(CAR_TO_ELEMENT)被误用于将汽车映射到元素和汽车组到元素。我定义了一个超类CarElement和子类CarUserElement和CarGroupElement。
  • 州是“活跃”或无趣的字符串
  • 我在其他地方设定了定义和陈述,我们不需要担心这一点。
  • 在连接表上使用DEP_NR。如果它为零,请使用USR_DEP_NR。我在本地SQL中成功地使用COALESCE(NULLIF())并且希望在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;

}

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

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)

其他hibernate用户似乎也有同样的问题:他们无法使用任何版本,请参阅此主题和其他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(即DEP_NR OR USR_DEP_NR仅使用@JoinColumns)连接这三个colulmns。但我需要这个coalesce(nullif())

3 个答案:

答案 0 :(得分:3)

我遇到了类似的问题,似乎问题是你在@Id中使用@Formula。 Hibernate希望Ids可以插入,而Formulas是只读的。

在我的情况下,我能够通过单独创建单独的列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;

}

答案 1 :(得分:0)

暂停加入公式在Hibernate中非常脆弱;我总是很难让他们正常工作。

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

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

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

答案 2 :(得分:0)

我也遇到了强制转换异常,并且我在Hibernate 5.x上。

在Hibernate花时间修复问题之前,我发现虽然this guy's approach可能不是最干净的(他甚至可以避免这个事实!),但它确实有效。

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

enter image description here