我有一个奇怪的问题,即hibernate不会在多对一关系中创建预期的实体类型。我们有以下具有子类层次结构(简化)的实体:
@Entity
@Table(name = "A")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class A {
@Id
...
public Long getId() { ... }
...
}
@Entity
@DiscriminatorValue("1")
public class A1 extends A {
...
}
@Entity
@DiscriminatorValue("2")
public class A2 extends A {
...
}
@Entity
@Table(name = "B")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DISCRIMINATOR", discriminatorType = DiscriminatorType.STRING, length = 1)
public abstract class B<AClass extends A> {
protected AClass a;
@Id
...
public Long getId() { ... }
...
public abstract AClass getA();
public void setA(AClass a) { ... }
}
@Entity
@DiscriminatorValue("1")
public class B1 extends B<A1> {
...
@Override
@ManyToOne(fetch = EAGER)
@JoinColumn(name = "A_ID")
public A1 getA() { ... }
}
@Entity
@DiscriminatorValue("2")
public class B2 extends B<A2> {
...
@Override
@ManyToOne(fetch = EAGER)
@JoinColumn(name = "A_ID")
public A2 getA() { ... }
}
在persistence.xml
中,两个实体都按顺序声明
A2
A1
B2
B1
现在我在DB中创建A1和B1的实例:
A1 a1 = new A1();
entityManager.persist(a1);
B1 b1 = new B1();
b1.setA(a1);
entityManager.persist(b1);
我可以看到实例正确保存到数据库中每个都有ID 1,DISCRIMINATOR也是1,B中的A_ID也是1.
当我现在尝试获取B(在另一个休眠会话中)时:
B b = entityManager.find(B.class, 1L);
我得到例外:
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没有考虑A表的DISCRIMINATOR列,但总是创建在配置中声明的第一个子类型。怎么解决这个问题?注释有问题吗?
(我在方法getA()
的具体实现中首先使用超类型B中的注释,但这会导致类似的问题。)
答案 0 :(得分:3)
您在A_ID
和B1
子类中使用相同的连接列(B2
)。
在每个子类中使用不同的一个:
@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() { ... }
}
虽然重用列可能是有意义的(根据子类,对于每个记录,不同的列无论如何都会null
),似乎Hibernate在内部使用列名来唯一地标识一些映射元素。同桌。这就是为什么它可能会忽略B1
中多对一映射的定义,并使用B2
中的映射来定义它(因为B2
在B1
之前定义{1}}中的{1}}。
答案 1 :(得分:3)
使用Hibernate 5.0.2.Final,我能够使用@ManyToOne(..., targetEntity = A.class)
使您的示例正常工作。我还用普通的getter替换了public abstract 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;
}
}
@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()
}
我在文档中没有找到任何关于此行为的信息。所以我只有我的观察:
targetEntity = A.class
Hibernate甚至没有查询表DISCRIMINATOR
的{{1}}列时,急切地从A
和A
一起提取行,就像它已经做出了B
的实际类型的决定。 A
时,targetEntity = A.class
出现在查询中,而对象是使用类A.DISCRIMINATOR
的正确子类创建的。答案 2 :(得分:1)
仅此一项,只是要补充一点,当您在与它们有关系的类中以相同的名称命名子类字段时,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签名。