问题
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