java Spring 数据-“手动”后端查询更新后刷新实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33825429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring data - Refresh entity after "manual" backend query update
提问by andPat
let's suppose to have this situation:
让我们假设有这种情况:
We have spring data configured in the standard way, there is a Respository
object, an Entity
object and all works well.
我们以标准方式配置了弹簧数据,有一个Respository
对象,一个Entity
对象并且一切正常。
Now for some complex motivations I have to use directly EntityManager
(or JdbcTemplate
, whatever is at a lower level than spring data) to update the table associated to my Entity
, with a native sql query. So I'm not using Entity
object, but simply doing a db update manually on the table I use as entity (is more correct to say the table from which I get values, see next rows).
The reason is that I had to bind my spring-data Entity
to a mysql view that makes UNION of multiple tables, not directly to table I need to update.
现在对于一些复杂的动机,我必须直接使用EntityManager
(或者JdbcTemplate
,任何低于 spring 数据的级别)来更新与 my 关联的表Entity
,使用本机 sql 查询。所以我没有使用Entity
对象,而只是在我用作实体的表上手动执行数据库更新(更正确地说是我从中获取值的表,请参阅下一行)。原因是我必须将我的 spring-data 绑定Entity
到一个 mysql 视图,该视图使多个表成为 UNION,而不是直接绑定到我需要更新的表。
What happens is:
发生的事情是:
In a functional test I call "manual" update method (on table from which the mysql view is created) previously described (through entity-manager) AND
If I make a simple Respository.findOne(objectId)
I get old object (not updated one). I've to call Entitymanager.refresh(object)
to get updated object.
在功能测试中,我调用先前描述的“手动”更新方法(在创建 mysql 视图的表上)(通过实体管理器),如果我做一个简单的Respository.findOne(objectId)
我得到旧对象(未更新)。我必须打电话Entitymanager.refresh(object)
来获取更新的对象。
Why?
为什么?
Is there a way to "synchronize" (out of the box) objects (or force some refresh) in spring-data? Or am I asking for a miracle? I'm not ironical, but maybe I'm not so expert, maybe (or probably) is my ignorance. If so please explain me why and (if you want) share some advanced knowledge about this amazing framework.
有没有办法在 spring-data 中“同步”(开箱即用)对象(或强制刷新)?还是我在寻求奇迹?我不是讽刺,但也许我不是那么专业,也许(或可能)是我的无知。如果是这样,请向我解释原因并(如果您愿意)分享有关这个惊人框架的一些高级知识。
回答by Alan Hay
If I make a simple Respository.findOne(objectId) I get old object (not updated one). I've to call Entitymanager.refresh(object) to get updated object.
Why?
如果我制作一个简单的 Respository.findOne(objectId) 我得到旧对象(未更新)。我必须调用 Entitymanager.refresh(object) 来获取更新的对象。
为什么?
The first-level cache is active for the duration of a session. Any object entity previously retrieved in the context of a session will be retrieved from the first-level cache unless there is reason to go back to the database.
一级缓存在会话期间处于活动状态。任何先前在会话上下文中检索到的对象实体都将从一级缓存中检索,除非有理由返回到数据库。
Is there a reason to go back to the database after your SQL update? Well, as the book Pro JPA 2 notes (p199) regarding bulk update statements (either via JPQL or SQL):
SQL 更新后是否有理由返回数据库?好吧,正如 Pro JPA 2 书中关于批量更新语句(通过 JPQL 或 SQL)的注释(p199):
The first issue for developers to consider when using these [bulk update] statements is that the persistence context is not updated to reflect the results of the operation. Bulk operations are issued as SQL against the database, bypassing the in-memory structures of the persistence context.
开发人员在使用这些 [bulk update] 语句时要考虑的第一个问题是持久化上下文没有更新以反映操作的结果。批量操作作为针对数据库的 SQL 发出,绕过了持久化上下文的内存结构。
which is what you are seeing. That is why you need to call refresh to force the entity to be reloaded from the database as the persistence context is not aware of any potential modifications.
这就是你所看到的。这就是为什么您需要调用 refresh 以强制从数据库重新加载实体,因为持久性上下文不知道任何潜在的修改。
The book also notes the following about using Native SQL statements (rather than JPQL bulk update):
本书还注意到有关使用 Native SQL 语句(而不是 JPQL 批量更新)的以下内容:
■ CAUTION Native SQL update and delete operations should not be executed on tables mapped by an entity. The JP QL operations tell the provider what cached entity state must be invalidated in order to remain consistent with the database. Native SQL operations bypass such checks and can quickly lead to situations where the inmemory cache is out of date with respect to the database.
■ 小心 不应在实体映射的表上执行本机 SQL 更新和删除操作。JP QL 操作告诉提供者必须使缓存的实体状态无效才能与数据库保持一致。本机 SQL 操作会绕过此类检查,并可能很快导致内存缓存相对于数据库已过时的情况。
Essentially then, should you have a 2nd level cache configured then updating any entity currently in the cache via a native SQL statement is likely to result in stale data in the cache.
基本上,如果您配置了二级缓存,那么通过本机 SQL 语句更新当前缓存中的任何实体可能会导致缓存中的数据过时。
回答by Narasimha A
In Spring Boot JpaRepository:
在 Spring Boot JpaRepository 中:
If our modifying query changes entities contained in the persistence context, then this context becomes outdated.
如果我们的修改查询更改了持久化上下文中包含的实体,那么这个上下文就会过时。
In order to fetch the entities from the database with latest record.
为了从具有最新记录的数据库中获取实体。
Use @Modifying(clearAutomatically = true)
使用@Modifying(clearAutomatically = true)
@Modifying annotation has clearAutomatically attribute which defines whether it should clear the underlying persistence context after executing the modifying query.
@Modifying 注释具有 clearAutomatically 属性,该属性定义在执行修改查询后是否应清除底层持久性上下文。
Example:
例子:
@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);
回答by geneqew
Based on the way you described your usage, fetching from the repo should retrieve the updated object without the need to refresh the object as long as the method which used the entity manager to merge has @transactional
根据您描述的使用方式,只要使用实体管理器合并的方法具有 @transactional,从 repo 中获取就应该检索更新的对象而无需刷新对象
here's a sample test
这是一个示例测试
@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());
}
}