Java 休眠在关系中创建错误的实体子类型

Java 休眠在关系中创建错误的实体子类型,java,hibernate,jpa,discriminator,Java,Hibernate,Jpa,Discriminator,我有一个奇怪的问题,hibernate没有在多对一关系中创建预期的实体类型。我们有以下具有子类层次结构(简化)的实体: 现在我在DB中创建A1和B1的实例: A1 a1 = new A1(); entityManager.persist(a1); B1 b1 = new B1(); b1.setA(a1); entityManager.persist(b1); 我可以看到实例被正确地保存到数据库中,每个实例的ID都是1,鉴别器也是1,B中的A_ID也是1 当我现在尝试获取B时(在另一个hibe

我有一个奇怪的问题,hibernate没有在多对一关系中创建预期的实体类型。我们有以下具有子类层次结构(简化)的实体:

现在我在DB中创建A1和B1的实例:

A1 a1 = new A1();
entityManager.persist(a1);
B1 b1 = new B1();
b1.setA(a1);
entityManager.persist(b1);
我可以看到实例被正确地保存到数据库中,每个实例的ID都是1,鉴别器也是1,B中的A_ID也是1

当我现在尝试获取B时(在另一个hibernate会话中):

我得到一个例外:

org.hibernate.PropertyAccessException: Exception occurred inside getter of B
Caused by: java.lang.ClassCastException: A2 cannot be cast to A1
at B1.getA(B1.java:61)
... 108 more 
通过调试,我发现hibernate正在创建类型B1的正确实体,并为与A的关系创建类型A2的错误实体。如果
persistence.xml
中的顺序发生更改,则会创建正确的类型A1。在本例中,hibernate似乎不考虑表的鉴别器列,而是始终创建配置中声明的第一个子类型。如何解决这个问题?注释有什么问题吗


(我也有方法
getA()
的具体实现,其注释最初在超类型B中,但这会导致类似的问题。)

您在
B1
B2
子类中使用相同的连接列(
A\u ID

在每个子类中使用不同的一个:

@Entity
@DiscriminatorValue("1")
public class B1 extends B<A1> {
    @Override
    @ManyToOne(fetch = EAGER)
    @JoinColumn(name = "A1_ID")
    public A1 getA() { ... }
}

@Entity
@DiscriminatorValue("2")
public class B2 extends B<A2> {
    @Override
    @ManyToOne(fetch = EAGER)
    @JoinColumn(name = "A2_ID")
    public A2 getA() { ... }
}
@实体
@鉴别器值(“1”)
公共类别B1扩展至B{
@凌驾
@manytone(fetch=EAGER)
@JoinColumn(name=“A1_ID”)
公共A1 getA(){…}
}
@实体
@鉴别器值(“2”)
公共类B2扩展到B{
@凌驾
@manytone(fetch=EAGER)
@JoinColumn(name=“A2_ID”)
公共A2 getA(){…}
}

尽管重用该列可能是有意义的(对于不同的列,根据子类的不同,每个记录的一个列都将是
null
),但Hibernate似乎在内部使用列名来唯一标识同一表中的一些映射元素。这就是为什么它可能会忽略
B1
中多对一映射的定义,并使用
B2
中的映射(因为
B2
是在
persistence.xml
B1
之前定义的)。

使用Hibernate 5.0.2。最后,我能够使用
@ManyToOne使您的示例工作(…,targetEntity=A.class)
。我还用普通的getter替换了
公共抽象AClass getA();

@Entity
@Table(name = "B")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class B<AClass extends A> {
    private Long id;
    private AClass a;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.EAGER, targetEntity = A.class)
    @JoinColumn(name = "A_ID")
    public AClass getA() {
        return a;
    }

    public void setA(AClass a) {
        this.a = a;
    }
}
@实体
@表(name=“B”)
@继承(策略=InheritanceType.SINGLE_表)
@鉴别器列(name=“DISCRIMINATOR”,discriminatorType=discriminatorType.STRING,长度=1)
B类公开摘要{
私人长id;
私人a类;
@身份证
@生成值
公共长getId(){
返回id;
}
公共无效集合id(长id){
this.id=id;
}
@manytone(fetch=FetchType.EAGER,targetEntity=A.class)
@JoinColumn(name=“A_ID”)
公共AClass getA(){
返回a;
}
公共无效setA(a类){
这个a=a;
}
}
@实体
@鉴别器值(“1”)
公共类别B1扩展至B{
//不需要重写getA()
}
@实体
@鉴别器值(“2”)
公共类B2扩展到B{
//不需要重写getA()
}
我在文档中没有找到任何关于这种行为的信息。因此,我只有我的观察结果:

  • 没有
    targetEntity=A.class
    Hibernate在急切地从
    A
    B
    中提取行时,甚至没有查询表
    A
    鉴别器列,就像它已经决定了
    A
    的实际类型一样
  • 当我添加
    targetEntity=A.class
    时,查询中出现了
    A.DISCRIMINATOR
    ,并使用class
    A
    的正确子类创建了对象

这篇文章很晚才开始,但要补充的是,当您在与Hibernate有关系的类中使用相同名称命名子类字段时,Hibernate会抛出相同的错误(返回错误的子类型)

@Entity
public abstract class Box {
    ...
}

@Entity
public class LargeBox extends Box {
    ...
}

@Entity
public class SmallBox extends Box {
    ...
}

@Entity
public class A {

    @ManyToOne
    private LargeBox box;

}

@Entity
public class B {

    @ManyToOne
    private SmallBox box;
}
当box转换为LargeBox时,从数据库中读取B类实例时,上述操作将引发错误。更新为:

@Entity
public class A {

    @ManyToOne
    private LargeBox largeBox;

}

@Entity
public class B {

    @ManyToOne
    private SmallBox smallBox;
}

…为我修复了它。注意:示例很简单,您也需要相应地更新getter签名。

谢谢您的回答。昨天我有机会测试了这一点,它在以下问题的场景中起作用:)targetEntity=A.class完成了这个技巧。你救了我一天。。谢谢:)谢谢德拉根的回答!:)我没有时间测试它是否有效,因为另一个答案有效,并且有一些优点:1。更少的代码。2.小于DB列3。我们可以在DB列上保留NOTNULL和外键约束。
@Entity
@Table(name = "B")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class B<AClass extends A> {
    private Long id;
    private AClass a;

    @Id
    @GeneratedValue
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.EAGER, targetEntity = A.class)
    @JoinColumn(name = "A_ID")
    public AClass getA() {
        return a;
    }

    public void setA(AClass a) {
        this.a = a;
    }
}
@Entity
@DiscriminatorValue("1")
public class B1 extends B<A1> {
    // no need to override getA()
}
@Entity
@DiscriminatorValue("2")
public class B2 extends B<A2> {
    // no need to override getA()
}
@Entity
public abstract class Box {
    ...
}

@Entity
public class LargeBox extends Box {
    ...
}

@Entity
public class SmallBox extends Box {
    ...
}

@Entity
public class A {

    @ManyToOne
    private LargeBox box;

}

@Entity
public class B {

    @ManyToOne
    private SmallBox box;
}
@Entity
public class A {

    @ManyToOne
    private LargeBox largeBox;

}

@Entity
public class B {

    @ManyToOne
    private SmallBox smallBox;
}