Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent Hibernate session from flushing/ storing invalid dirty entities

I would like to know which approach to take to prevent Hibernate 4.3.4 (with Spring and Hibernate Vaidator) from flushing dirty entities. In my code, i use a manual implementation of Hibernate Validator (a .validate() method within the instance itself), which gets called just before saving an entity. The validate() method returns a list of errors, if any are found, otherwise a Session.update() is called to store the entity, followed by a commit of the transaction.

This works, but when the instance itself is manipulated (posted/ request parameters are set in the entity) the entity and corresponding Hibernate session are marked 'dirty' and the entity is stored with the next Session.flush().

In my case, i would like to have explicit control over the entities which may be stored and prevent any dirty entity from being stored, how would i achieve this?

EDIT:

I know i can manually regulate this by evicting an entity (or clearing and reintroducing an entity via a merge), but this is not my aim. Rather than having to regulate the persistency manually, i would like to have the offset situation that no entity which was not explicitly saved and of which the transaction was not explicitly committed, will not get stored to the database (e.g. via an interceptor?).

like image 413
Marius Avatar asked Apr 22 '14 08:04

Marius


1 Answers

If you modify entities and explicitly want them not to be flushed, you can detach them before modifying. The detached entities will no longer be managed by the persistence context.

Hibernate API:

session.evict(myEntity)

JPA:

entityManager.detach(myEntity)

EDIT: In case you want to detach all entities and manage only some of them, you can clear your persistence context first and then merge the entites you do need to be managed:

Hibernate API:

session.clear();
managedEntity = session.merge(detachedEntity);

JPA:

entityManager.clear();
managedEntity = entityManager.merge(detachedEntity);

EDIT 2 All changes to managed entities are flushed on transaction commit. I am not aware of any feature of JPA or Hibernate that can turn this behavior off. So in addition to detaching some or all entities, you have some other choices, but none is exactly what you are looking for:

  • fetching the entities outside a transaction, so they are detached immediately. This seems to come closest to what you want - no hassle with managed changes, only explicit merging would save an entity and you would have to deal less with the persistence API. However you would still need to open a session to merge the entities you do want to be saved with changes.

  • mapping query results to DTOs/POJOs (which are always detached) using the NEW operator in your queries. This approach has the advantage of separating the persistence mapping from the application. However introducing a bunch of new classes may not be worth it, and not doing it consistently across the application adds conceptual complexity.

  • working inside a transaction, but rolling back instead of committing. You would not be able to save anything, and it is kind of a crude way to prevent changes from being synced with the DB. Unfortunately, you have to either commit or rollback a transaction eventually.

  • creating deep copies of the entities to change them. Frankly, this doesn't make much sense to me neither, just adding it for the sake of completeness.

EDIT 3 Though not specified by the JPA spec, both Hibernate and Eclipselink allow marking transactions or even result sets from a single query as read-only. This could be useful for you, as

When a persistent object is read-only, Hibernate does not dirty-check simple properties

Change flushing gets a bit more complicated when it comes to relationships. Please refer to this documentation for Hibernate or this one for Eclipselink.

like image 68
kostja Avatar answered Oct 16 '22 05:10

kostja