8

I have the following service:

@Service
public class CamelService {

    @Transactional
    public aCamelThing() {

          Camel camel = this.camelRepository.findOne(1);

          System.out.println(camel.getCamelName());  // prints "normalCamel"

          // simple hql set field 'camelName'
          int res = this.camelRepository.updateCamelName(1,"superCamel!");

          Camel camelWithNewName = this.camelRepository.findOne(1);

          System.out.println(camelWithNewName .getCamelName());  // prints "normalCamel" ?!?!?

    } 
}

Any idea how can i achieve the goal that the second println will print: "superCamel!" ? (separating the second call to a new transaction is not ideal).

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
Urbanleg
  • 6,252
  • 16
  • 76
  • 139
  • Not sure what your updateCamelName is doing (probably issuing a HQL query?). But you need to at least flush your changes and probably clear the repository, else you will get the already retrieve entity. Also why aren't you simply updating the `camel` with the name and storing that?! – M. Deinum Mar 20 '14 at 12:55
  • In addition to what M. Deinum said updateCamelName should also commit the transaction. Is the method annotated with @Transactional – Dhanush Gopinath Mar 20 '14 at 12:57
  • 1
    Deinum, I tried to camelRepository.flush() but still the last find doesnt bring the new name. why i dont update simply the object? my real life use case is much more complicated than the one i posted, its only for the sake of simplicity. Dhanush, the method is transactional so why should the repository be? – Urbanleg Mar 20 '14 at 13:04

1 Answers1

21

The reason you see this working as it works is quite simple: JPA is defined to work that way.

I am assuming you trigger an update query for updateCamelName(…). The JPA specification states the following for update and delete operations:

The persistence context is not synchronized with the result of the bulk update or delete.

Caution should be used when executing bulk update or delete operations because they may result in inconsistencies between the database and the entities in the active persistence context. In general, bulk update and delete operations should only be performed within a transaction in a new persistence con- text or before fetching or accessing entities whose state might be affected by such operations.

This means, that if you need to see the changes of such an operation you need to do the following things:

  1. Clear the EntityManager after this operation. Spring Data JPA's @Modifying annotation has a clearAutomatically flag defaulting to false. If that is set to true, invoking the query method will clear the EntityManager automatically (as the name suggests. Use that with caution, as it will effectively drop all pending changes that have not been flushed to the database yet!
  2. Re-obtain a fresh instance of the entity from the EntityManager. Calling findOne(…) on the repository seems like a reasonable way to do this as this roughly translates into EntityManager.find(…). Be aware that this might still hit 2nd level caches configured on the persistence provider.

The safest way to work around this is - as the spec suggests - to use update queries only for bulk operations and fall back to the "load entity, alter, merge" approach by default.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211