【发布时间】:2017-12-29 10:05:54
【问题描述】:
这里有一个奇怪的情况,Hibernate 试图实现一个实体,但它不存在。
我们有两个实体,A 和 B,大致如下:
public class A {
...
@Id
@NotNull
@Column(name = "aId")
private int id;
@OneToOne(mappedBy = "a", cascade = CascadeType.PERSIST)
@OptimisticLock(excluded = true)
private B b;
}
public class B {
...
@Id
@NotNull
@Column(name = "aId")
private int id;
@JoinColumn(name = "bId", insertable = false, updatable = false)
@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
private A a;
B 有一个原始的int 列,它由一个整数类型的非空数据库列支持。我们正在为A 的单个实例执行EntityManager#find。由于B 被急切地加载(根据@OneToOne 的默认值),此查找被转换为一条语句,将A- 和B- 表与公共ID 上的左外连接连接起来。
这段代码从大约 2004 年开始就一直在工作,没有任何问题。现在不行了。
症状是javax.persistence.PersistenceException,文字说明,null 值已分配给失败的原始值。
stacktrace 清楚地表明,在这种情况下,我们查找单个数字主键。尽管如此,hibernate 记录的语句是由以下 where 部分构成的:a0_.AID in (?, ?) 其中参数绑定到两个不同的 ID。我们无法判断这是否正常,但是接下来会发生两个As,一个带有B,一个没有。日志显示,对于第一行,B 的所有字段都存在,并生成了完整的A 实例(包括子实体B)。在下一步中,解析第二行。在这里,所有B 列都是空的,除了id(外连接)。尽管如此,一旦hibernate尝试将null放入int字段,就会生成B子实体。
我们使用的是 Hibernate 4.1.12.Final(是的,我们也很惭愧,不,更新是不可能的),所有这些都发生在带有最新驱动程序包的 Oracle 12c 上。一切都部署在 Wildfly 9 上(嗯,不会变得更好,我们知道)。
问题是:
- hibernate 到底是如何得出这样的结论,即搜索一个
A应该通过输入两个 ID 来完成? - 第二个 ID 来自哪里?
- 为什么 Hibernate 尝试创建一个 B 实例,但没有可创建的实例?
亲切的问候, 乔纳森
编辑
我们发现,根本原因似乎是,有时 Oracle 返回 ID 为 B,虽然表中没有条目,所以基本上select * from A left outer join B on A.aId = B.aId where A.aid = 1 返回一行 B.aId = 1 尽管 select * from B where B.aid = 1 返回一个空的行集。
我们假设这是 Oracle RDBMS 中的一个错误,并尝试通过重新启动服务器和重新导入架构来解决它。
Hibernate 解释 ID 的存在,因此它假定整个实体都在那里,尝试填充实体并在此过程中用 null 填充 int 字段。
这未解决:为什么 Hibernate 使用 in 子句进行选择,第二个 ID 来自哪里?
【问题讨论】: