Different SQL using find and createQuery from Hibernate entity manager

妖精的绣舞 提交于 2019-12-11 04:43:36

问题


I'm using Hibernate 3.3.0.GA and I noticed some strange behavior, not documented (I think).

I noticed that entityManager.find resolve the EAGER relationships of my entity and entityManager.createQuery not.

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

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"):

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. Just use mvn clean install and the console will print the queries.

Updated

@KlausGroenbaek said the EclipseLink 2.5.2 have the same behaviour in the two methods. 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).


回答1:


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.

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.

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. If you insert em.clear() or use a different Persistence Context (EntityManager) you will see the select because the address is not already loaded.

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. 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.

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. Much of it I learned the hard way (which for me was debugging and reading the EclipseLink source code); 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.



来源:https://stackoverflow.com/questions/41679264/different-sql-using-find-and-createquery-from-hibernate-entity-manager

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!