【问题标题】:Different SQL using find and createQuery from Hibernate entity manager使用 Hibernate 实体管理器中的 find 和 createQuery 的不同 SQL
【发布时间】:2017-05-31 11:04:14
【问题描述】:

我正在使用 Hibernate 3.3.0.GA,我注意到一些奇怪的行为,没有记录(我认为)。

我注意到entityManager.find 解决了我的实体的EAGER 关系,而entityManager.createQuery 没有。

例如,如果我有一个实体:

@Entity
public class Person {

     @Id
     private int id;

     private String name;

     @ManyToOne //EAGER by default in JPA
     private Address address;

}

生成的带有entityManager.find(Person.class, 1L)的SQL:

select
    person0_.id as id1_1_,
    person0_.address_id as address3_1_1_,
    person0_.name as name1_1_,
    address1_.id as id2_0_ 
from
    Person person0_ 
left outer join
    Address address1_ 
        on person0_.address_id=address1_.id 
where
    person0_.id=?

以及生成的带有entityManager.createQuery("SELECT p FROM Person where id = 1")的SQL:

select
    person0_.id as id1_,
    person0_.address_id as address3_1_,
    person0_.name as name1_ 
from
    Person person0_ 
where
    person0_.id=?

那么,有没有解释为什么会发生这种情况?对我来说,两者都需要具有相同的行为。

工作示例

我使用 Hibernate 4.1.6.Final:https://github.com/dherik/hibernate-find-em-so-question 在我的存储库中创建了一个示例来显示该问题。只需使用mvn clean install,控制台就会打印查询。

更新

@KlausGroenbaek 说 EclipseLink 2.5.2 在这两种方法中具有相同的行为。他也做了一个 Hibernate 示例,并在两种方法中获得了相似的结果(find 它执行连接提取,createQuery 它执行多重选择,两者都是 EAGER 的定义)。

【问题讨论】:

  • 两个查询确实具有相同的行为,至少从人员数据在两者中相同的角度来看。您是否有特定的代码有问题?
  • 加载 Person 后,它必须具有有效的 Address 实例或 null。 ManyToOne 只有在使用编织创建 Address 子类时才能偷懒。您确定没有第二次查询选择地址,也许它没有记录?
  • 嗨@TimBiegeleisen,是的,我有代码(模拟在那里)。遗憾的是,我无法分享代码,但我可以在我的 github 中创建一个示例并分享。
  • @KlausGroenbaek,我敢肯定。此特定结果在 Eclipse 的“显示”选项卡中重现,在“调试”期间,它适用于同一应用程序中的任何实体。
  • 我用 EclipseLink 2.5.2 (JavaSE) 做了一个测试,使用 em.find() 和 em.createQuery() 没有区别,日志完全一样。您是否对两个调用都使用相同的 EntityManager,如果是,您是否记得在两者之间调用 EntityManager.clear() 来清除缓存的实体(因为我忘记了,然后查询根本没有生成 SQL)。跨度>

标签: java hibernate hibernate-entitymanager


【解决方案1】:

我查看了您的示例,在修复它之后,它的工作方式与 HiberNate 5.2.5 find() 使用连接和 createQuery 使用多重选择完全一样。

您在示例中没有看到这一点的原因是因为您在数据库中有 NO DATA,find 仍然会进行连接,但是因为数据库中没有 Person,所以它没有不需要查找任何地址,因此缺少第二个选择。

但是,您会注意到,即使您将数据添加到示例中,从地址中选择的地址也不会显示。这是因为当您使用 find() 时,该地址已经在持久性上下文中,因此无需再次加载它 - 事实上,JPA 必须为该地址返回完全相同的 Java 实例,如果它已经加载到持久性上下文中。如果您插入 em.clear() 或使用不同的持久性上下文 (EntityManager),您将看到选择,因为地址尚未加载。

您的示例只是一个示例,但是绝对禁止将应用程序管理的 EntityManager 存储在字段中的方式,当您自己管理它们时,它们应该始终是局部变量。如果您使用容器管理的 EntityManager (Spring JavaEE),它们可以是使用 @PersistenceContext 注入时的字段,但这只是因为注入的 EntityManager 是一个代理,它存储状态是一个 ThreadLocal。

我从 2009 年开始广泛使用 JPA,一开始我犯了很多错误,因为我不明白持久性上下文到底是什么,所以我有重叠的 EntityManagers,未维护的双向关系,而且我没有完全理解了解实体状态,或容器与应用程序管理的 EntityManager。其中大部分是我通过艰难的方式学习的(对我来说是调试和阅读 EclipseLink 源代码);回想起来,我绝对应该买一本书来了解全局,而不是通过随机谷歌搜索我认为问题所在来解决问题。对于尚未阅读 JPA 文档、好书或部门文章的任何人,请帮自己一个忙,从我的错误中吸取教训。

【讨论】:

  • 嗨!你做了什么修复?我颠倒了查询的顺序,findcreateQuery 仍然具有相同的行为。我在findcreateQuery 之后使用了em.clear(),也没有什么不同。
  • 我已经分叉了你的项目,将它转换为 gradle(我拒绝使用 Maven 及其冗长的 XML 配置),并修复了问题,以便你可以看到查询。同样,您的问题是,如果数据库中没有数据,您将看不到所有查询。 github.com/QwertGold/hibernate-find-em-so-question
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
  • 2012-03-08
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
相关资源
最近更新 更多