不同 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 文档、一本好书或专业文章的人,帮自己一个忙并从我的错误中吸取教训。
我正在使用 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 文档、一本好书或专业文章的人,帮自己一个忙并从我的错误中吸取教训。