34

I have a method annotated with @Transactional. I retrieve an object from my DB, change a field, and then return from the method. Without saving my object, the database gets updated anyway which is strange.

Could you please tell me how to avoid this beahvior?

chidar
  • 405
  • 1
  • 5
  • 9

2 Answers2

30

This behaviour is one of the main purposes of transactionality.

Before the transactional method is about to return, the transaction commits, meaning all changes to the managed entities are flushed to the database.

If an error occurs, the transaction will be rolled back, meaning that no changes will be committed to the database.

You are probably getting the LazyInitializationException when trying to access a lazily loaded property, probably a collection from an entity. Lazily loded properties do not get instantiated when you fetch an entitiy from DB.

If you access a lazily loaded property in a transaction, the persistence provider will create a query, instantiate the result and attach it to the 'parent' entity.

EDIT: If you want to have the lazy properties loaded AND be able to change your entity without the changes being persisted to the DB, you can fetch the entity with fetch joins for the lazy properties.

em.createQuery("SELECT e FROM MyEntity e JOIN FETCH e.lazyProp");

Then proceed with one of the methods described by @orid.

If you are not using fetch joins, you will need to access the lazily loaded properties while still inside the transaction:

myEntity.getLazyProp().size();

Note the call to size(). Calling the getter is not enough as you will get a proxy. You need to perform an operation that needs the actual data from the property.

kostja
  • 60,521
  • 48
  • 179
  • 224
  • 1
    Very well put. Do you have any suggestions for reference materials on the topic? – Kevin Bowersox Feb 04 '14 at 12:44
  • Thanks, Kevin. The only resource I can recommend is the [Pro JPA 2 Book](http://www.apress.com/9781430219569) since this is what I used for learning JPA. Chapter 6, EntityManager / Transaction Management is probably the best place to look up. The same knowledge can probably be found in the Hibernate and Eclipselink docs as well. – kostja Feb 04 '14 at 12:52
  • Thanks kosjta for your answer very useful, I had in head the idea that without calling update data won't be commited to DB. thanks again :) – chidar Feb 04 '14 at 14:26
  • You're welcome :) Yeah, container-managed JPA does some less-than-obvoius tricks behind the scenes. I can really recommend the Pro JPA 2 book I linked above. – kostja Feb 04 '14 at 14:41
  • 1
    Hello kosjta, I'm back just to tell you about a thing. Even if i used readOnly=true in order to avoid LazyInitializationException and in the same time not to update database, actually database gets updated anyway ! Any idea please ? – chidar Feb 05 '14 at 11:42
  • I am not sure I understand. Would you mind creating a new question on this, since it is beyond the scope of the current question? – kostja Feb 05 '14 at 12:52
30

This a normal JPA behavior.

Once you retrieve an object via find() or so, that object is regarded as attached, or belongs to a persistence context. Once you exit the method the @Transactional triggers a Spring transaction management aspect which flushes every "dirty" object to database and commits the transaction. Since your object is already changed within the context of the persistence context and the transaction, the changes are saved to the database even without the need to explicitly call a save method.

If you want to change your object without affecting the database, you have two options:

  1. Update the field after returning from the method annotated with @Transactional
  2. If withing the method, call detach on the entity manager
Ori Dar
  • 18,687
  • 5
  • 58
  • 72