绕过托管实体或不绕过JPA

时间:2015-09-29 22:29:17

标签: hibernate jpa

为什么JPA EntityManager执行可变查询,例如UPDATE直接针对DB,但是选择首先针对下面的持久化上下文(又名二级缓存)执行SELECT查询(使用Hibernate 5测试)?

似乎JPA spec没有规定这样做。是否只是为了获得更好的性能而存在不一致的风险?

/* ... */

String name = em.find(Person.class, 1).getName();

em.getTransaction().begin();
em.createQuery("update Person set name='new' where id=1")
  .executeUpdate(); 
em.getTransaction().commit(); // DB updated but the entity not

Person p = em
  .createQuery("select p from Person p where id=1", Person.class)
  .getSingleResult(); // the stale entity returned

assertEqual(name, p.getName()); // true

1 个答案:

答案 0 :(得分:1)

可能出于性能原因,如果JPQL查询匹配已在内存中的实体管理器中加载的任何实体,则规范不需要使用DB中的当前状态刷新内存中的实体。

在您的示例中,这是在后台发生的事情 - 从DB检索具有id 1的人员(它现在存在于EntityManager缓存中) - 通过JPQL在DB中更新人名(实体保持不变) - 要从DB检索具有id 1的实体人员 - 但是实体管理器中已存在id为1的实体,因此返回此实例(名称中包含旧值) - 实体未与DB合并。有关详细信息,请参阅回答this question

此行为符合JPA要求,即在单个EntityManager中,DB中的单行不能有2个实体实例。如何解决它只有两个选项,一个是将现有实体与DB中的状态合并,另一个是忽略DB中的数据。 JPA规范选择了第二种选择。

如果无法检索开头时ID为1的人,则会在选择查询中使用新值检索新实体。

在实体中获取新数据的解决方案可能是在%% // DISPLAY - Surf and Scatter figure hs = surf(X,Y,Z,C,'EdgeColor','interp','FaceColor','none','Marker','none') ; hold on hp = scatter(xt,yt,25,c,'filled','LineWidth',1.5) ; colormap(hsv(nColor)) %// choose a more distintive colormap (any other colormap will work) colorbar view(2) update查询之间使用em.clean(),但请注意它将清除缓存中的所有实体,而不仅仅是person(id = 1)实体,它可能有其他副作用。

但是,更安全的解决方案是在使用更新脚本更改实体后使用select