EntityManager refresh() 方法不刷新而是重置

EntityManager refresh() method does not refresh but resets

下面代码中的em.refresh(person)不起作用。它不是使用数据库中的新值刷新 person,而是重置(撤消或丢弃)在缓存中所做的更改。我无法理解为什么?

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(); 

有人可以通过 EntityManagerrefresh() 方法帮助我理解这种行为吗?

如果您想在一个事务中查看其他事务所做的更改,您需要将隔离级别更改为 READ_COMMITTED

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

几个定义来澄清讨论:

  • 可重复读取:本质上意味着在一个事务中数据库将看到相同的值,除非数据在该事务中被修改
  • Hibernate flush:会话中所做的修改(例如 person.setAge(24);)在刷新之前对数据库不可见。刷新发生在调用 em.flush、提交或通常执行诸如 select * from Person where name=... 的查询时,但调用 refresh()
  • 时不会
  • Hibernate 刷新:从数据库中读取数据并使用该数据更新 session/1st 级缓存。

所以基本上:

  • 您正在通过调用 setAge() 修改年龄,但该更改未刷新,因此数据库不可见
  • 您正在从另一个会话更新,但该更改不可见,因为事务是隔离的(除非使用 READ_COMMITTED)
  • 当调用刷新时,数据库不知道调用了 setAge() 并将更新与另一个事务隔离,因此它显示 23