简体   繁体   English

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

[英]Different SQL using find and createQuery from Hibernate entity manager

I'm using Hibernate 3.3.0.GA and I noticed some strange behavior, not documented (I think). 我正在使用Hibernate 3.3.0.GA,我注意到一些奇怪的行为,没有记录(我认为)。

I noticed that entityManager.find resolve the EAGER relationships of my entity and entityManager.createQuery not. 我注意到, entityManager.createQuery无法解析我的实体的EAGER关系,而entityManager.find无法解决。

Per example, if I have an entity: 例如,如果我有一个实体:

@Entity
public class Person {

     @Id
     private int id;

     private String name;

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

}

The generated SQL with entityManager.find(Person.class, 1L) : 使用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=?

And the generated SQL with entityManager.createQuery("SELECT p FROM Person where id = 1") : 以及使用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=?

So, is there a explanation about why this happen? 那么,有没有关于为什么发生这种情况的解释? For me, both need to have the same behavior. 对我来说,两者都必须具有相同的行为。

Working example 工作实例

I create an example in my repository showing that problem, using Hibernate 4.1.6.Final: https://github.com/dherik/hibernate-find-em-so-question . 我使用Hibernate 4.1.6.Final在我的存储库中创建了一个显示该问题的示例: https : //github.com/dherik/hibernate-find-em-so-question Just use mvn clean install and the console will print the queries. 只需使用mvn clean install ,控制台将打印查询。

Updated 更新

@KlausGroenbaek said the EclipseLink 2.5.2 have the same behaviour in the two methods. @KlausGroenbaek表示EclipseLink 2.5.2在这两种方法中具有相同的行为。 He did a Hibernate example too and obtain the similar result in the two methods ( find it does a join fetch and createQuery it does multiple selects, both EAGER by definition). 他也做了一个Hibernate示例,并且在两种方法中都获得了相似的结果( find它执行createQuery提取,而createQuery则进行多次选择,从定义createQuery都是EAGER )。

I have looked at your example and after fixing it, it works exactly like HiberNate 5.2.5 find() uses a join and createQuery uses multiple selects. 我查看了您的示例,并对其进行了修复,其工作原理与HiberNate 5.2.5 find()使用createQuery并且createQuery使用多个选择一样。

The reason you don't see this in your example is because you have NO DATA in the database, find still does a join, but because there is no Person in the DB, it doesn't need to look for any Address, so the second select is missing. 在您的示例中看不到该信息的原因是因为您的数据库中没有DATA ,find仍然进行联接,但是由于DB中没有Person,因此不需要查找任何Address,因此第二选择丢失。

However, you will notice that even if you add data to your example, the select from address does not show up. 但是,您会注意到,即使将数据添加到示例中,从地址选择也不会显示。 This is because the address is already in the Persistence Context from when you used find(), so there is no need to load it again - In fact JPA MUST return the exact same Java instance for the address if it is already loaded in the Persistence Context. 这是因为从使用find()以来,该地址已经在Persistence Context中,因此无需再次加载它-实际上,如果JPA 必须已经在Persistence中加载了该地址,则JPA 必须返回该地址的完全相同的Java实例。语境。 If you insert em.clear() or use a different Persistence Context (EntityManager) you will see the select because the address is not already loaded. 如果插入em.clear()或使用其他持久性上下文(EntityManager),则会看到选择内容,因为该地址尚未加载。

Your example is of cause just an example, but the way you store an application managed EntityManager in a field, is absolutely forbidden, they should always be local variables when you manage them your self. 您的示例只是一个示例,但是绝对禁止在字段中存储应用程序管理的EntityManager的方式,当您自己管理自己时,它们应始终是局部变量。 If you use a container managed EntityManager (Spring JavaEE) they can be fields when injected using @PersistenceContext, but that is only because the injected EntityManager is a proxy which stores the state is a ThreadLocal. 如果使用容器管理的EntityManager(Spring JavaEE),则在使用@PersistenceContext进行注入时,它们可以是字段,但这仅是因为注入的EntityManager是存储状态的代理,它是ThreadLocal。

I have used JPA extensively since 2009, and I made a lot of mistakes in the beginning because I didn't understand exactly what a Persistence Context was, so I had overlapping EntityManagers, unmaintained bidirectional relations, and I didn't fully understand Entity states, or container vs application managed EntityManager. 自2009年以来,我已经广泛使用了JPA,一开始我犯了很多错误,因为我不完全了解持久性上下文是什么,所以我有重叠的EntityManager,未维护的双向关系,而且我还没有完全理解实体状态,或容器与应用程序托管的EntityManager。 Much of it I learned the hard way (which for me was debugging and reading the EclipseLink source code); 我从中学到了很多困难的方法(对我来说,这是调试和阅读EclipseLink源代码)。 looking back I should definitely have bought a book to get the big picture, instead of solving problems by randomly Googling what I though the issue was. 回顾过去,我绝对应该买一本书以获取全局,而不是通过随机搜索我所遇到的问题来解决问题。 For anyone how have not read the JPA documentation, a good book, or in dept article, do your self a favor and learn from my mistakes. 对于没有读过JPA文档,一本好书或一篇专业文章的人,请帮自己一个忙,并从我的错误中学习。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM