Search code examples
hibernatejpaentitymanagerhibernate-jpa

EntityManager refresh() method does not refresh but resets


The em.refresh(person) in the following code does not work. Rather than refreshing the person with a fresh value from database, it resets (undo or discards) the changes made in the cache. I am not able to understand why?

em.getTransaction().begin();
        
Person person = em.find(Person.class, 2L); //Person[id=2L, age=23]
person.setAge(24); 
System.out.println(person.getAge()); //it prints 24
        
//Person with id=2 in database gets modified concurrently somehow,
//its age becomes 25 in PERSON table (by an SQL update for example "UPDATE person SET age=25 WHERE id=2")

em.refresh(person); // attempts to load fresh value from database with a SELECT...
System.out.println(person.getAge()); //it prints 23, rather than 25, why?
        
em.getTransaction().commit();
em.close(); 

Could someone help me understand this behavior by the refresh() method of EntityManager?


Solution

  • If you want to see changes made from other transactions while inside a transaction you need to change the isolation level to READ_COMMITTED

    <property name="hibernate.connection.isolation">TRANSACTION_READ_COMMITTED</property>
    

    A few definitions to clarify the discussion:

    • Repeatable read: essentially means that within a transaction the database will see the same value, unless the data was modified within that transaction
    • Hibernate flush: the modifications made in the session (e.g. person.setAge(24);) are not visible by the database until they are flushed. A flush occurs when calling em.flush, committing or, typically, when executing a query such as select * from Person where name=... but not when calling refresh()
    • Hibernate refresh: reading the data from the database and updating the session/1st level cache with that data.

    So basically:

    • You're modifying the age by calling setAge() but that change is not flushed, hence not visible by the database
    • You're updating from another session but that change is not visible either because the transactions are isolated (unless using READ_COMMITTED)
    • When refresh is called the database is not aware that setAge() was called and it isolates the update from another transaction, so it shows 23