我正在使用Hibernate 3.3.0.GA,我发现了一些奇怪的行为,没有记录(我认为)。
我注意到Vary: *
解析了我的实体的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")
那么,有没有解释为什么会发生这种情况?对我来说,两者都需要有相同的行为。
工作示例
我在我的存储库中创建了一个示例,显示了该问题,使用了Hibernate 4.1.6.Final:https://github.com/dherik/hibernate-find-em-so-question。只需使用select
person0_.id as id1_,
person0_.address_id as address3_1_,
person0_.name as name1_
from
Person person0_
where
person0_.id=?
,控制台就会打印查询。
更新
@KlausGroenbaek表示EclipseLink 2.5.2在这两种方法中具有相同的行为。他也做了一个Hibernate示例并在两个方法中获得了类似的结果(mvn clean install
它进行了连接提取,而find
它进行了多次选择,根据定义都是createQuery
。
答案 0 :(得分:2)
我查看了您的示例,在修复后,它的工作原理与HiberNate 5.2.5完全相同find()
使用了连接,createQuery
使用了多个选择。
您在示例中没有看到这个的原因是因为您在数据库中有 NO DATA ,find仍然进行连接,但是因为DB中没有Person,所以它没有需要查找任何地址,因此缺少第二个选择。
但是,您会注意到即使您向示例添加数据,也不会显示从地址中选择的地址。这是因为当你使用find()时,地址已经在持久化上下文中了,所以不需要再次加载它 - 实际上JPA 必须返回完全相同的Java实例。它已经加载到持久化上下文中。如果您插入em.clear()
或使用不同的持久性上下文(EntityManager),您将看到select,因为该地址尚未加载。
您的示例只是一个示例,但是您在一个字段中存储应用程序管理的EntityManager的方式是绝对禁止的,当您自己管理它们时,它们应始终是本地变量。如果使用容器管理的EntityManager(Spring JavaEE),则在使用@PersistenceContext注入时可以是字段,但这只是因为注入的EntityManager是存储状态的代理是ThreadLocal。
我从2009年开始广泛使用JPA,并且我在开始时犯了很多错误,因为我并不完全理解持久化上下文是什么,所以我有重叠的EntityManagers,没有维护的双向关系,我没有完全理解实体状态,或容器与应用程序管理的EntityManager。我学到了很多东西(对我来说是调试和阅读EclipseLink源代码);回头看我绝对应该买一本书才能全面了解,而不是通过随机谷歌搜索问题来解决问题。对于任何人来说,如果没有阅读JPA文档,一本好书,或者在部门文章中,请帮助自己,并从错误中吸取教训。