【问题标题】:Hibernate materializing non-existing EntityHibernate 实现不存在的实体
【发布时间】:2017-12-29 10:05:54
【问题描述】:

这里有一个奇怪的情况,Hibernate 试图实现一个实体,但它不存在。

我们有两个实体,AB,大致如下:

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 上(嗯,不会变得更好,我们知道)。

问题是:

  1. hibernate 到底是如何得出这样的结论,即搜索一个 A 应该通过输入两个 ID 来完成?
  2. 第二个 ID 来自哪里?
  3. 为什么 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 来自哪里?

【问题讨论】:

    标签: java oracle hibernate


    【解决方案1】:

    问题 1 和 2 的答案很简单:Hibernate 将多个选择推迟到可能的最新时刻,并在需要执行时在一个语句中执行它们。所以我们有两个选择,其中一个不需要立即执行。这是第二个 ID。

    至于3:这是Oracle DBMS中同时修复的错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-31
      • 2012-09-25
      • 2010-10-05
      • 1970-01-01
      • 2020-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多