为什么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
答案 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
。