Spring数据 - 在"手动"后端查询更新后刷新实体

and*_*Pat 7 java persistence spring-data spring-data-jpa

让我们假设有这种情况:

我们以标准方式配置弹簧数据,有一个Respository对象,一个Entity对象,一切运行良好.

现在,对于一些复杂的动机,我必须直接使用EntityManager(或者JdbcTemplate,比弹簧数据更低的级别)Entity使用本机sql查询来更新与我相关联的表.所以我没有使用Entity对象,只是简单地在我用作实体的表上手动进行数据库更新(更准确地说是从中获取值的表,请参阅下一行).原因是我必须将我的spring-data绑定Entity到一个mysql视图,该视图使UNION成为多个表,而不是直接到我需要更新的表.

会发生什么:

在功能测试中,我称之为"手动"更新方法(在创建mysql视图的表上)(通过实体管理器)和如果我做一个简单的Respository.findOne(objectId)我得到旧对象(没有更新一个).我打电话Entitymanager.refresh(object)来获取更新的对象.

为什么?

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

Ala*_*Hay 7

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

为什么?

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

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

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

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

本书还注意到以下有关使用 Native SQL 语句(而不是 JPQL 批量更新)的内容:

? 小心 不应在实体映射的表上执行本机 SQL 更新和删除操作。JP QL 操作告诉提供者必须使缓存的实体状态无效才能与数据库保持一致。本机 SQL 操作会绕过此类检查,并可能迅速导致内存缓存相对于数据库已过时的情况。

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


gen*_*qew 0

根据您描述用法的方式,只要使用实体管理器合并的方法具有@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());
    }

}
Run Code Online (Sandbox Code Playgroud)