让我们假设有这种情况:
我们以标准方式配置了弹簧数据,有一个Respository
对象,一个Entity
对象,一切运行良好。
现在,对于一些复杂的动机,我必须直接使用EntityManager
(或JdbcTemplate
,无论是低于弹簧数据的水平),以更新与Entity
相关联的表格, 本机SQL查询。所以我没有使用Entity
对象,而是简单地在我用作实体的表上手动执行数据库更新(更准确地说是从中获取值的表,请参阅下一行)。
原因是我必须将我的spring-data Entity
绑定到一个mysql视图,该视图使UNION成为多个表,而不是直接到我需要更新的表。
会发生什么:
在功能测试中,我打电话给"手册"更新方法(在创建mysql视图的表上)之前描述的(通过实体管理器)AND
如果我做一个简单的Respository.findOne(objectId)
我得到旧对象(没有更新)。我打电话给Entitymanager.refresh(object)
以获取更新的对象。
为什么?
有没有办法同步"同步" (开箱即用)弹簧数据中的对象(或强制刷新)?还是我要求奇迹? 我并不讽刺,但也许我不那么专业,也许(或可能)是我的无知。如果是这样,请解释我为什么和(如果你想)分享一些关于这个惊人框架的高级知识。
答案 0 :(得分:4)
如果我创建一个简单的Respository.findOne(objectId),我会得到旧对象(不是 更新一个)。我要调用Entitymanager.refresh(object)来获取 更新的对象。
为什么?
第一级缓存在会话期间处于活动状态。除非有理由返回数据库,否则将从第一级缓存中检索先前在会话上下文中检索的任何对象实体。
有没有理由在SQL更新后返回数据库?好吧,正如关于批量更新语句(通过JPQL或SQL)的Pro JPA 2(p199)一书所述:
开发人员在使用这些[批量更新]语句时要考虑的第一个问题 是持久性上下文未更新以反映结果 的操作。批量操作是作为SQL发布的 数据库,绕过持久性的内存结构 上下文强>
这就是你所看到的。这就是为什么你需要调用refresh来强制从数据库重新加载实体的原因,因为持久化上下文不知道任何潜在的修改。
本书还提到了有关使用Native SQL语句(而不是JPQL批量更新)的以下内容:
■注意不应该使用本机SQL更新和删除操作 在由实体映射的表上执行。 JP QL操作告诉了 提供者必须使缓存的实体状态无效 与数据库保持一致。本机SQL操作绕过这种情况 检查并可以快速导致内存缓存的情况 关于数据库的过时。
基本上,如果您配置了第二级缓存,则通过本机SQL语句更新当前缓存中的任何实体可能会导致缓存中的陈旧数据。
答案 1 :(得分:0)
根据您描述用法的方式,只要使用实体管理器合并的方法具有@transactional
,从repo获取应该检索更新的对象而无需刷新对象这是一个样本测试
@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());
}
}
答案 2 :(得分:0)
在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);