Hibernate 在关系中创建错误的实体子类型
Hibernate creating wrong entity subtype in relationship
我有一个奇怪的问题,即 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
现在我在数据库中创建 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 table 的 DISCRIMINATOR 列,但总是创建配置中声明的第一个子类型。这怎么能解决?注释有问题吗?
(我最初也有方法getA()
的具体实现,它的注解在超类型B中,但这会导致类似的问题。)
您在 B1
和 B2
子类中使用相同的连接列 (A_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() { ... }
}
尽管重用该列可能有意义(对于不同的列,根据子类,每个记录无论如何都会 null
),Hibernate 似乎在内部使用列名来唯一标识某些映射元素在同一个 table 内。这就是为什么它可能会忽略 B1
中多对一映射的定义,并使用 B2
中的映射(因为 B2
是在 B1
之前定义的在 persistence.xml
).
使用 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 甚至没有查询 table A
的 DISCRIMINATOR
列,当急切地从 A
和 [=19] 中获取行时=],就像它已经决定了 A
的实际类型一样。
- 当我添加
targetEntity = A.class
时,A.DISCRIMINATOR
出现在查询中,并且对象是使用 class A
的右子 class 创建的.
晚了,只是补充一点,当您在具有关系的 classes 中命名具有相同名称的 subclass 字段时,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 时,当从数据库中读取 class B 的实例时,上面会抛出一个错误。更新到:
@Entity
public class A {
@ManyToOne
private LargeBox largeBox;
}
@Entity
public class B {
@ManyToOne
private SmallBox smallBox;
}
...帮我修好了。注意:示例很简短,您也需要相应地更新 getter 签名。
我有一个奇怪的问题,即 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
现在我在数据库中创建 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 table 的 DISCRIMINATOR 列,但总是创建配置中声明的第一个子类型。这怎么能解决?注释有问题吗?
(我最初也有方法getA()
的具体实现,它的注解在超类型B中,但这会导致类似的问题。)
您在 B1
和 B2
子类中使用相同的连接列 (A_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() { ... }
}
尽管重用该列可能有意义(对于不同的列,根据子类,每个记录无论如何都会 null
),Hibernate 似乎在内部使用列名来唯一标识某些映射元素在同一个 table 内。这就是为什么它可能会忽略 B1
中多对一映射的定义,并使用 B2
中的映射(因为 B2
是在 B1
之前定义的在 persistence.xml
).
使用 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 甚至没有查询 tableA
的DISCRIMINATOR
列,当急切地从A
和 [=19] 中获取行时=],就像它已经决定了A
的实际类型一样。 - 当我添加
targetEntity = A.class
时,A.DISCRIMINATOR
出现在查询中,并且对象是使用 classA
的右子 class 创建的.
晚了,只是补充一点,当您在具有关系的 classes 中命名具有相同名称的 subclass 字段时,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 时,当从数据库中读取 class B 的实例时,上面会抛出一个错误。更新到:
@Entity
public class A {
@ManyToOne
private LargeBox largeBox;
}
@Entity
public class B {
@ManyToOne
private SmallBox smallBox;
}
...帮我修好了。注意:示例很简短,您也需要相应地更新 getter 签名。