不同 SQL 使用 Hibernate 实体管理器中的 find 和 createQuery

Different SQL using find and createQuery from Hibernate entity manager

我正在使用 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;

}

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

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=?

和生成的 SQL 与 entityManager.createQuery("SELECT p FROM Person where id = 1"):

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)。

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

你在你的例子中没有看到这个的原因是因为你在数据库中没有 NO DATA,find 仍然进行连接,但是因为没有 Person DB,它不需要查找任何地址,因此缺少第二个 select。

但是,您会注意到,即使您将数据添加到您的示例中,select 发件人地址也不会显示。这是因为当您使用 find() 时该地址已经在持久性上下文中,因此无需再次加载它 - 实际上 JPA​​ MUST return如果地址已经加载到持久性上下文中,则为地址的相同 Java 实例。如果您插入 em.clear() 或使用不同的持久性上下文 (EntityManager),您将看到 select,因为该地址尚未加载。

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

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