pbuchheit
pbuchheit

Reputation: 1607

Best Practice For Verifying an Entity Exists Using Hibernate

In one of my applications I have a validator that is responsible for checking if an entity with a given class and id exists in the database. In the past I used entityManager.find() and simply checked if the result was null. While this works, it is expensive for entities with complex associations since find() will try to pull back the whole entity graph. It is also prone to lazy load exceptions if it ever gets used outside the scope of a transaction.

I've been looking for an alternative solution and I have found a few suggestions but I don't know how to evaluate them in terms of performance vs potential down sides. From the research I have done so far, here are the potential options I see:

  1. Manually create and execute a 'count' query.
  2. Use Criteria Builder to create and execute a count query.
  3. Use entityManager.getReference()

3 would be my preferred solution if it actually worked, but it doesn't seem to be viable in Hibernate. Hibernate doesn't throw an exception when getReference() is called on an entity that does not exist in the DB and I don't see any way to differentiate between the proxy returned when the lookup fails and the proxy returned when the lookup actually finds something.

If anyone is familiar with Hibernate, is there another way of doing this that I'm missing? If not, is the performance overhead from using a Criteria Query high enough to make a native query a better option?

Upvotes: 0

Views: 2733

Answers (1)

meriton
meriton

Reputation: 70574

It depends.

EntityManager.find(id) is entirely adequate if the entity has no eager associations, and a reasonable size (i.e. contains no huge blobs or lengthy strings). Since a primary key index is usually clustered, loading the state of the entity does not cause additional I/O for the database, and incurs a negligible CPU and bandwidth overhead. On the positive side, EntityManager.find(id) is easy to use, easy to understand, and, by virtue of loading the entity into the session, can speed up future accesses to that entity.

If your entity is very big, or has eager associations, a count query is more efficient. Whether you write this in SQL or through the Criteria API has negligible performance impact, since the bottleneck will usually be database I/O, not CPU time on your application server.

Sometimes, it is also possible to omit the check for existence, and trust the database to alert you if a constraint is violated. This is more efficient for the database, and more robust in the face of high concurrency. And that's what entityManager.getReference(id) is for:

person.getFriends().add(entityManager.getReference(newFriendId)); 
// assume the friend id is valid - if it isn't, the database will tell us anyway

appendix: existence checks and phantom reads

In the read committed isolation level, another transaction may delete an entity after its existence has been checked by a query, but before the transaction commits:

Thread 1                                                Thread 2

select count(*)
from BigFatEntity
where id = 42;

1 row found

                                                        delete from BigFatEntity
                                                        where id = 42;
                                                        commit;

insert into Relationship(id, big_fat_entity_id)
values (123, 42);

constraint violation: 42 no longer exists

That is, checking for existence before using an entity as a foreign key can not prevent a foreign key violation in all cases.

Even under serializable transaction isolation, checking first will not prevent a transaction rollback in this case.

That is, checking for existence will not catch all cases, but makes the transaction longer, increasing the probability of transaction rollback and reducing the overall throughput of the system.

Upvotes: 1

Related Questions