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

时间:2017-01-16 15:00:56

标签: java hibernate hibernate-entitymanager

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

1 个答案:

答案 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文档,一本好书,或者在部门文章中,请帮助自己,并从错误中吸取教训。