频道
bg

Guice Account Scope

coding九月 09, 20211mins
Java Hibernate

场景H2

存在实体A、B,A包含一个和B的一对多关系,即

java

A {
@OneToMany
List<B>
}

现在需要假设需要更新A中的某个普通属性,更新完后返回一个包含关联关系B的A对象。

需要注意的是,这里业务场景对A的更新,不是通过设置一个托管状态的A的set方法实现的,而是merge一个页面提交上来的A,因此这个A本身是不包含的关联关系的值的。

现象H2

调用entityManager.merge 返回的A的关联关系B的值是空的,通过懒加载获取B无法触发。

重新查询无法正常获取到关联属性,框架会返回一级缓存中的更新时对象(没有关联属性)

原因H2

下面的原因只是推测,没有得到证实

merge的时候数据库中的代理对象不会被设置到merge的结果

如果是关联关系的owner则需要更新成当前的关联值

如果不是关联关系的owner那设置了也无妨

所以结论上讲,关联关系的值以是以merge的当前对象为主的。

再次查询关联没有被更新

以当前一级缓存的状态为主,如果关联关系只是之前没fetch,新的查询出来的关联关系是有fetch的值的,那fetch的值会被更新上去。

但如果一级缓存中的关联关系都是空的,那么即便新的查询出来的关联关系是有fetch的值也不会被更新上去。

WorkaroundH2

merge完后由于没有关联属性值,后续的查询也无法把关联值设置进去,这个问题可以进行一次refresh。但需要注意的是merge完后需要手动flush,否则更新状态会被fresh覆盖

  1. merge
  2. flush
  3. refresh
  4. fetch query

其他一级缓存相关的问题H2

如果查询过一次A并且把B fetch出来了,然后直接删除B,再次查询A,返回的A依然会包含刚才删除的B,因为一级缓存中的A包含了删除之前的B。

如果一开没有fetch B,那么再次触发的查询,能够查询到正确的删除后的结果。但是这里依然是有个注意点,删除B后,一定要手动flush,因为如果使用find 查询是不会自动flush的(如果第二次的A查询使用JPQL则没有问题)。

By default, Hibernate uses the AUTO flush mode which triggers a flush in the following circumstances:

  • prior to committing a Transaction
  • prior to executing a JPQL/HQL query that overlaps with the queued entity actions
  • before executing any native SQL query that has no registered synchronization

find 和JPQL对一级缓存的处理有有些不同:

  1. find 如果一些缓存里有缓存,则直接不查询
  2. JPQL 无论如果都会去查询,然后和一级缓存中的数据进行合并。

评论


新的评论

匹配您的Gravatar头像

Joen Yu

@2022 JoenYu, all rights reserved. Made with love.