mortenoh
mortenoh

Reputation: 255

How to not persist an object that is part of an active session (and is mapped)

For a project I'm working on, i'm adding import/export functionality. This is working OK, no issues.

What I'm trying to do now, is to have an option called "dryRun" which allows the user to test the import before actually doing anything, and just give an report back.

So, my setup is:

  1. Getting the import data through spring mvc (with OpenSessionInView)
  2. Deserialize this into my domain classes (which are the actual mapped hibernate classes)
  3. Scan through everything and make sure that references etc are ok.
  4. All my services have a @Transactional around them

This is mostly OK, but somewhere.. a save is triggered (i never call save), and my grouping object is complaining about a transient reference.. which is correct, since it is not saved.. I assume this is some kind of default mode hibernate has, since the grouping object references another object that is mapped.. So how can I make sure that hibernate never saves anything? without removing the @Transactional and OpenSessionInView (which is needed for the normal case)

Any ideas?

Regards, Morten

Upvotes: 0

Views: 261

Answers (2)

Aaron Digulla
Aaron Digulla

Reputation: 328604

You say

somewhere.. a save is triggered

That "somewhere" is in the stack trace which you get with the exception.

[EDIT] The stack trace basically says "the method MetaDataController.importXml() is annotated with @Transactional". When it returns, Hibernate tries to commit the transaction as it should and then, it stumbles over an unsaved dependency in OrganisationUnitGroup (and yes, it would be nice if it would say which field contains the dependency because Hibernate knows this, too).

There are several solutions:

  1. Create two methods to import. The new one, .importXmlDryRun() must not have the @Transactional annotation. That might work but my gut feeling warns me about problems even though I couldn't name them right now.

  2. Throw an exception in .importXml() to abort the transaction when dry run is requested and ignore this exception in the default handler. The exception will cause Hibernate to roll back. This is the safest solution.

  3. Get the hibernate session and call clear() (docs)

Upvotes: 2

Stefan Steinegger
Stefan Steinegger

Reputation: 64628

Hibernate stores changes in entities implicitly. This is a part of its persistence ignorance, which is actually very important. (See this question.) You can turn it off by setting the flush mode to Never, but it is usually not recommended. If you have queries in your import code, they wouldn't consider changes in memory and return different results (which could destroy the purpose of a dry run).

Options:

  • Keep you implementation and rollback everything at the end. It only work if you don't want to change something in the database at the same time (eg. a log that a dry-run happened).
  • Set flush mode = Never and avoid calling "save" and "update". It's kind of hacky.
  • Pull everything that is important for the dry-run out to a validation class which does not change any entities and only run this one. This is the most complex but also cleanest solution.

Upvotes: 2

Related Questions