I have come across a situation (which I think is weird but is possibly quite normal) where I use the EntityManager.getReference(LObj.getClass(), LObj.getId()) to get a database entity and then pass the returned object to be persisted in another table.
So basically the flow was like this:
class TFacade{ createT(FObj, AObj) { T TObj = new T(); TObj.setF(FObj); TObj.setA(AObj); ... EntityManager.persist(TObj); ... L LObj = A.getL(); FObj.setL(LObj); FFacade.editF(FObj); } } @TransactionAttributeType.REQUIRES_NEW class FFacade{ editF(FObj){ L LObj = FObj.getL(); LObj = EntityManager.getReference(LObj.getClass(), LObj.getId()); ... EntityManager.merge(FObj); ... FLHFacade.create(FObj, LObj); } } @TransactionAttributeType.REQUIRED class FLHFacade{ createFLH(FObj, LObj){ FLH FLHObj = new FLH(); FLHObj.setF(FObj); FLHObj.setL(LObj); .... EntityManager.persist(FLHObj); ... } }
I was getting the following exception "java.lang.IllegalArgumentException: Unknown entity: com.my.persistence.L$$EnhancerByCGLIB$$3e7987d0"
After looking into it for a while, I finally figured out that it was because I was using the EntityManager.getReference() method that I was getting the above exception as the method was returning a proxy.
This makes me wonder, when is it advisable to use the EntityManager.getReference() method instead of the EntityManager.find() method?
EntityManager.getReference() throws an EntityNotFoundException if it cant find the entity being searched for which is very convenient in itself. EntityManager.find() method merely returns null if it cant find the entity.
With regards to transaction boundaries, sounds to me like you would need to use the find() method before passing the newly found entity to a new transaction. If you use the getReference() method then you would probably end up in a situation similar to mine with the above exception.
Because a reference is 'managed', but not hydrated, it can also allow you to remove an entity by ID, without needing to load it into memory first.
As you can't remove an unmanaged entity, it's just plain silly to load all fields using find(...) or createQuery(...), only to immediately delete it.
I disagree with the selected answer, and as davidxxx correctly pointed out, getReference does not provide such behaviour of dynamic updations without select. I asked a question concerning the validity of this answer, see here - cannot update without issuing select on using setter after getReference() of hibernate JPA.
I quite honestly haven't seen anybody who's actually used that functionality. ANYWHERE. And i don't understand why it's so upvoted.
Now first of all, no matter what you call on a hibernate proxy object, a setter or a getter, an SQL is fired and the object is loaded.
But then i thought, so what if JPA getReference() proxy doesn't provide that functionality. I can just write my own proxy.
Now, we can all argue that selects on primary keys are as fast as a query can get and it's not really something to go to great lengths to avoid. But for those of us who can't handle it due to one reason or another, below is an implementation of such a proxy. But before i you see the implementation, see it's usage and how simple it is to use.
USAGE
And this would fire the following query -
and even if you want to insert, you can still do PersistenceService.save(new Order("a", 2)); and it would fire an insert as it should.
IMPLEMENTATION
Add this to your pom.xml -
Make this class to create dynamic proxy -
Make an interface with all the methods -
Now, make an interceptor which will allow you to implement these methods on your proxy -
And the exception class -
A service to save using this proxy -
I usually use getReference method when i do not need to access database state (I mean getter method). Just to change state (I mean setter method). As you should know, getReference returns a proxy object which uses a powerful feature called automatic dirty checking. Suppose the following
If i call find method, JPA provider, behind the scenes, will call
If i call getReference method, JPA provider, behind the scenes, will call
And you know why ???
When you call getReference, you will get a proxy object. Something like this one (JPA provider takes care of implementing this proxy)
So before transaction commit, JPA provider will see stateChanged flag in order to update OR NOT person entity. If no rows is updated after update statement, JPA provider will throw EntityNotFoundException according to JPA specification.
regards,
As I explained in this article, assuming you have a parent
Post
entity and a childPostComment
as illustrated in the following diagram:If you call
find
when you try to set the@ManyToOne
post
association:Hibernate will execute the following statements:
The SELECT query is useless this time because we don’t need the Post entity to be fetched. We only want to set the underlying post_id Foreign Key column.
Now, if you use
getReference
instead:This time, Hibernate will issue just the INSERT statement:
Unlike
find
, thegetReference
only returns an entity Proxy which only has the identifier set. If you access the Proxy, the associated SQL statement will be triggered as long as the EntityManager is still open.However, in this case, we don’t need to access the entity Proxy. We only want to propagate the Foreign Key to the underlying table record so loading a Proxy is sufficient for this use case.
When loading a Proxy, you need to be aware that a LazyInitializationException can be thrown if you try to access the Proxy reference after the EntityManager is closed. For more details about handling the
LazyInitializationException
, check out this article.EntityManager.getReference()
is really an error prone method and there is really very few cases where a client code needs to use it.Personally, I never needed to use it.
EntityManager.getReference() and EntityManager.find() : no difference in terms of overhead
I disagree with the accepted answer and particularly :
It is not the behavior that I get with Hibernate 5 and the javadoc of
getReference()
doesn't say such a thing :EntityManager.getReference()
spares a query to retrieve the entity in two cases :1) if the entity is stored in the Persistence context, that is the first level cache.
And this behavior is not specific to
EntityManager.getReference()
,EntityManager.find()
will also spare a query to retrieve the entity if the entity is stored in the Persistence context.You can check the first point with any example.
You can also rely on the actual Hibernate implementation.
Indeed,
EntityManager.getReference()
relies on thecreateProxyIfNecessary()
method of theorg.hibernate.event.internal.DefaultLoadEventListener
class to load the entity.Here is its implementation :
The interesting part is :
2) If we don't effectively manipulate the entity, echoing to the lazily fetched of the javadoc.
Indeed, to ensure the effective loading of the entity, invoking a method on it is required.
So the gain would be related to a scenario where we want to load a entity without having the need to use it ? In the frame of applications, this need is really uncommon and in addition the
getReference()
behavior is also very misleading if you read the next part.Why favor EntityManager.find() over EntityManager.getReference()
In terms of overhead,
getReference()
is not better thanfind()
as discussed in the previous point.So why use the one or the other ?
Invoking
getReference()
may return a lazily fetched entity.Here, the lazy fetching doesn't refer to relationships of the entity but the entity itself.
It means that if we invoke
getReference()
and then the Persistence context is closed, the entity may be never loaded and so the result is really unpredictable. For example if the proxy object is serialized, you could get anull
reference as serialized result or if a method is invoked on the proxy object, an exception such asLazyInitializationException
is thrown.It means that the throw of
EntityNotFoundException
that is the main reason to usegetReference()
to handle an instance that does not exist in the database as an error situation may be never performed while the entity is not existing.EntityManager.find()
doesn't have the ambition of throwingEntityNotFoundException
if the entity is not found. Its behavior is both simple and clear. You will never have surprise as it returns always a loaded entity ornull
(if the entity is not found) but never an entity under the shape of a proxy that may not be effectively loaded.So
EntityManager.find()
should be favored in the very most of cases.