【问题标题】:EntityManager refresh() method does not refresh but resetsEntityManager refresh() 方法不刷新而是重置
【发布时间】:2021-11-17 11:21:51
【问题描述】:

以下代码中的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() 方法帮助我理解这种行为吗?

【问题讨论】:

  • 刷新前可以使用flush吗?
  • 您在事务中进行这些测试,事务通常带有“可重复读取”。除非数据库已更新(例如执行 flush ),否则它将在第二次选择时返回相同的值
  • @Alien 和 @Guillaume :在em.refresh(person) 之前发出em.flush() 将发出UPDATE... 更新数据库中的age=24,所以使用em.refresh(person) 我不会得到age=25 ,我会得到它age=24。因此em.refresh(person) 之前的em.flush() 无助于从数据库中获取更新的数据。
  • @Guillaume em.refresh(person) 没有提供可重复读取,它正在撤消。它正在丢弃缓存中所做的更改(age 从 23 更改为 24)并将person 对象返回到它首次加载到缓存中时的状态(age=23)。所以em.refresh(person) 也没有提供可重复读取,它正在重置/撤消person
  • @skip 您确定更改UPDATE person SET age=25 WHERE id=2 已提交吗?顺便问一下,你用什么数据库?

标签: hibernate jpa entitymanager hibernate-jpa


【解决方案1】:

如果您想在事务内部查看其他事务所做的更改,您需要将隔离级别更改为 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

【讨论】:

  • 我明白,但em.refresh(person) 甚至没有提供可重复读取,它只是重置/撤消缓存中所做的更改,将age 设置回23,@987654326 @person 第一次从数据库加载到缓存中时。
  • 我不确定您的期望,但这就是可重复读取的含义:您首先读取 23,然后您正在读取第二次(刷新时),它再次读取 23,因为默认情况下,事务是隔离的。你一直在说缓存,你有没有开启二级缓存?
  • Repeatable-Read 表示将从缓存中读取数据,并且在缓存中age 已更新为24,因此repeatable-read 表示age 应该被读取为24 而不是23. age 返回值 23 表示 personem.refresh(person) 之后的状态已重置回其首次从数据库加载到缓存时的初始状态。所以em.refresh(person) 基本上撤消了在缓存中所做的任何更改,并且没有给出可重复读取。
  • 不,我没有开启二级缓存。只是一级缓存。
  • 我已经更新了我的答案,可重复读取是在数据库的上下文中。刷新不是从一级缓存中读取
猜你喜欢
  • 2018-10-01
  • 2011-04-29
  • 2021-03-20
  • 2011-08-15
  • 1970-01-01
  • 1970-01-01
  • 2020-11-10
  • 2015-01-30
相关资源
最近更新 更多