Java JpaRepository 缓存新创建的对象。如何刷新?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28146783/
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
JpaRepository caches newly created object. How to refresh it?
提问by Sergei Ledvanov
I have a JpaRepository persisting newly created entity in Spring MVC app. This entity looks like this (very simplified):
我有一个 JpaRepository 在 Spring MVC 应用程序中持久化新创建的实体。这个实体看起来像这样(非常简化):
@Entity
public class Translation {
.....
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
private Version version;
....
}
and Version entity:
和版本实体:
@Entity
public class Version {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private long id;
@Column(name = "name")
private String name;
@Column(name = "version_code")
private long code;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "version", cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Translation> translations;
}
I create a translation object like this
我创建了一个这样的翻译对象
TranslationDTO t = new TranslationDTO();
t.setText(translationText);
ClientVersionDTO version = new ClientVersionDTO();
version.setId(11);
t.setVersion(version);
where 11 is a version that exists in the database already from the very beginning. Please notice that I do not set values for name
and code
of ClientVersionDTO.
其中 11 是从一开始就存在于数据库中的版本。请注意,我没有设置ClientVersionDTO 的name
和值code
。
Then I have a service that persists new object (I use dozer
library to convert DTO to entities)
然后我有一个持久化新对象的服务(我使用dozer
库将 DTO 转换为实体)
@Service
@Transactional
public class TranslationsServiceImpl implements TranslationsService {
@Override
public Long create(TranslationDTO translationDTO) {
Translation translation = translationsConverter.unconvert(translationDTO);
Translation t = translationRepository.saveAndFlush(translation);
Translation t2 = translationRepository.findOne(t.getId());
// !!!! t2.getVersion() returns version where no values are set to 'code' and 'name'
return t2.getId();
}
}
Please notice my comment "t2.getVersion() returns version where no values are set to 'code' and 'name'" - I was expecting so that when I fetch the data from the database, I would get a Version
object right from the database with code
and name
values set. However they are not set. So basically what I get as a t2.getVersion()
object is the same object as in input argument translationDTO.getVersion()
. How can they I re-invalidate the Version
object?
请注意我的评论“t2.getVersion() 返回没有值设置为‘代码’和‘名称’的版本”——我期待这样当我从数据库中获取数据时,我会从数据库中获取一个Version
对象与code
和name
值集。但是它们没有设置。所以基本上我作为t2.getVersion()
对象得到的对象与输入参数中的对象相同translationDTO.getVersion()
。他们如何使Version
对象重新无效?
UPDATEtried moving @Transactional to JpaRepository, but still the same result.
UPDATE尝试将 @Transactional 移动到 JpaRepository,但结果仍然相同。
采纳答案by manish
If you are using Hibernate, this is the expected result. When you call translationRepository.saveAndFlush(translation)
and translationRepository.findOne(t.getId())
one after the other, they hit the same Hibernate session which maintains a cache of all objects that it has worked on. Therefore, the second call simply returns the object passed to the first. There is nothing in those two lines that would have forced Hibernate to fire a SELECT
query on the database for the Version
entity.
如果您使用的是 Hibernate,这就是预期的结果。当你打电话translationRepository.saveAndFlush(translation)
,并translationRepository.findOne(t.getId())
一前一后,他们打它保持的所有对象,它已经在工作的高速缓存中的同一个Hibernate Session。因此,第二次调用简单地返回传递给第一个的对象。这两行中没有任何内容会迫使 HibernateSELECT
为Version
实体触发对数据库的查询。
Now, the JPA spec does have a refresh
method on the EntityManager
interface. Unfortunately, Spring Data JPA does not expose this method using its JpaRepository
interface. If this method was available, you could have done t = translationRepository.saveAndFlush(translation)
and then versionRepository.refresh(t.getVersion())
to force the JPA provider to synchronize the version entity with the database.
现在,JPA 规范确实refresh
在EntityManager
接口上有一个方法。不幸的是,Spring Data JPA 没有使用它的JpaRepository
接口公开这个方法。如果此方法可用,您可以完成t = translationRepository.saveAndFlush(translation)
然后versionRepository.refresh(t.getVersion())
强制 JPA 提供程序将版本实体与数据库同步。
Implementing this method is not difficult. Just extend SimpleJpaRepository
class from Spring Data JPA and implement the method yourself. For details see adding custom behaviour to all Spring Data JPA repositories.
实现这个方法并不困难。只需SimpleJpaRepository
从 Spring Data JPA扩展类并自己实现该方法。有关详细信息,请参阅向所有 Spring Data JPA 存储库添加自定义行为。
An alternate would be to load the version entity as versionRepository.findOne(version.getId())
before setting it on the translation. Since you can hard-code version id in your code, your versions seem to be static. You can therefore mark your Version
entity as @Immutable
and @Cacheable
(the former is a Hibernate-specific annotation). That way, versionRepository.findOne(version.getId())
should not hit the database every time it is called.
另一种方法是像versionRepository.findOne(version.getId())
在翻译上设置它之前一样加载版本实体。由于您可以在代码中硬编码版本 ID,因此您的版本似乎是静态的。因此,您可以将您的Version
实体标记为@Immutable
and @Cacheable
(前者是 Hibernate 特定的注释)。这样,versionRepository.findOne(version.getId())
每次调用时都不应该访问数据库。