【问题标题】:How to refresh entity after "manual" backend query update [duplicate]“手动”后端查询更新后如何刷新实体[重复]
【发布时间】:2016-02-22 20:48:16
【问题描述】:

假设有这种情况:

我们以标准方式配置了 Spring Data,有一个 Respository 对象,一个 Entity 对象,一切正常。

现在,出于一些复杂的动机,我必须使用EntityManager(或JdbcTemplate,任何级别低于 Spring Data)直接使用本机 SQL 查询更新与我的Entity 关联的表。所以,我没有使用Entity 对象,而只是在我用作实体的表上手动进行数据库更新(更正确地说是我从中获取值的表,请参阅下一行)。

原因是我必须将我的 spring-data Entity 绑定到一个 MySQL 视图,该视图使多个表成为 UNION,而不是直接绑定到我需要更新的表。

会发生什么:

在功能测试中,我调用“手动”更新方法(在创建 MySQL 视图的表上),如前所述(通过实体管理器),如果我做一个简单的Respository.findOne(objectId),我会得到旧的对象(未更新)。我必须调用Entitymanager.refresh(object) 来获取更新的对象。

为什么?

有没有办法在 spring-data 中“同步”(开箱即用)对象(或强制刷新)?还是我在祈求奇迹? 我并不讽刺,但也许我不是那么专家,也许(或可能)是我的无知。如果是这样,请解释我为什么并(如果你愿意)分享一些关于这个惊人框架的高级知识。

【问题讨论】:

  • 您是否在同一测试范围内执行更新和提取?
  • 在同一个测试中是的。
  • 那么(调用findOne 时对象未刷新)是预期的行为。假设您使用 Hibernate 作为 JPA 提供程序(所有 JPA 提供程序的逻辑相同),在单个测试期间使用相同的 Hibernate Session 和 Hibernate Session 缓存对象。 EntityManager.refresh 强制 Hibernate 忽略缓存内容并重新从数据库加载数据。
  • 在真实环境(已部署的应用程序)中运行时会发生什么?我想在这种情况下也有一个缓存。我如何(最终)禁用它?
  • 如果你的实际代码是@Transactional ... update(...),然后是Entity fetch(...),那么你就可以了,因为update(你实际执行更新的地方)和fetch(你获取更新的地方data) 位于两个不同的事务边界内,因此不会共享相同的Session,即使它们被称为update(...); Entity entity = fetch(...);。如果它们在同一个事务边界内,那么它们将共享同一个Session,并且您将拥有一个陈旧的对象。

标签: java persistence spring-data spring-data-jpa


【解决方案1】:

如果我做一个简单的 Respository.findOne(objectId) 我得到旧对象(不是 更新了一个)。我必须调用 Entitymanager.refresh(object) 来获取 更新的对象。

为什么?

一级缓存在会话期间处于活动状态。之前在会话上下文中检索到的任何对象实体都将从一级缓存中检索,除非有理由返回数据库。

您的 SQL 更新后是否有理由返回数据库?好吧,正如 Pro JPA 2 书中关于批量更新语句(通过 JPQL 或 SQL)的注释 (p199):

开发人员在使用这些 [批量更新] 语句时首先要考虑的问题 是持久化上下文没有更新以反映结果 的操作。批量操作作为 SQL 针对 数据库,绕过持久性的内存结构 上下文

这就是你所看到的。这就是为什么您需要调用 refresh 来强制从数据库中重新加载实体,因为持久性上下文不知道任何潜在的修改。

本书还对使用 Native SQL 语句(而不是 JPQL 批量更新)进行了以下说明:

■ 小心 本机 SQL 更新和删除操作不应 在由实体映射的表上执行。 JP QL 操作告诉 提供者必须使缓存的实体状态无效才能 与数据库保持一致。本机 SQL 操作绕过此类 检查并可能很快导致内存缓存被占用的情况 数据库过时了。

基本上,如果您配置了二级缓存,那么通过本机 SQL 语句更新当前缓存中的任何实体可能会导致缓存中的数据过时。

【讨论】:

    【解决方案2】:

    在 Spring Boot JpaRepository 中:

    如果我们的修改查询更改了持久化上下文中包含的实体,那么这个上下文就会过时。

    为了从数据库中获取具有最新记录的实体。

    使用@Modifying(clearAutomatically = true)

    @Modifying 注解具有 clearAutomatically 属性,该属性定义在执行修改查询后是否应清除底层持久性上下文。

    例子:

    @Modifying(clearAutomatically = true)
            @Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
        int expireNetwork(@Param("network_id") Integer network_id,  @Param("network_status") String network_status);
    

    【讨论】:

      【解决方案3】:

      根据您描述使用的方式,只要使用实体管理器合并的方法具有@transactional,从存储库中获取应该检索更新的对象而无需刷新对象

      这是一个示例测试

      @DirtiesContext(classMode = ClassMode.AFTER_CLASS)
      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(classes = ApplicationConfig.class)
      @EnableJpaRepositories(basePackages = "com.foo")
      public class SampleSegmentTest {
      
          @Resource
          SampleJpaRepository segmentJpaRepository;
      
          @PersistenceContext
          private EntityManager entityManager;
      
          @Transactional
          @Test
          public void test() {
              Segment segment = new Segment();
              ReflectionTestUtils.setField(segment, "value", "foo");
              ReflectionTestUtils.setField(segment, "description", "bar");
      
              segmentJpaRepository.save(segment);
      
              assertNotNull(segment.getId());
              assertEquals("foo", segment.getValue());
              assertEquals("bar",segment.getDescription());
      
              ReflectionTestUtils.setField(segment, "value", "foo2");
              entityManager.merge(segment);
      
              Segment updatedSegment = segmentJpaRepository.findOne(segment.getId());
              assertEquals("foo2", updatedSegment.getValue());
          }
      
      }
      

      【讨论】:

      • 感谢您的回复!完美,这就是我的情况!但是有一点(或大)的差异。而不是entityManager.merge(segment); 我做entityManager.createNativeQuery( "UPDATE segment_table set value = 'foo2' WHERE segment_id = 1" ).executeUpdate(); 我认为这可能是问题所在。但我无法更改查询。
      猜你喜欢
      • 2021-06-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2023-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-11
      相关资源
      最近更新 更多