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?
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.
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:
- Update the field after returning from the method annotated with
@Transactional
- If withing the method, call detach on the entity manager