2

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

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

4

1 回答 1

2

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

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

一些定义来澄清讨论:

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

所以基本上:

  • 您正在通过调用 setAge() 修改年龄,但该更改未刷新,因此数据库不可见
  • 您正在从另一个会话进行更新,但该更改也不可见,因为事务是隔离的(除非使用 READ_COMMITTED)
  • 当 refresh 被调用时,数据库不知道 setAge() 被调用并且它将更新与另一个事务隔离,所以它显示 23
于 2021-09-24T09:33:59.130 回答