JpaRepository 缓存新创建的对象。如何刷新呢?
JpaRepository caches newly created object. How to refresh it?
我有一个 JpaRepository 在 Spring MVC 应用程序中保存新创建的实体。这个实体看起来像这样(非常简单):
@Entity
public class Translation {
.....
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
private Version version;
....
}
和版本实体:
@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;
}
我创建了这样一个翻译对象
TranslationDTO t = new TranslationDTO();
t.setText(translationText);
ClientVersionDTO version = new ClientVersionDTO();
version.setId(11);
t.setVersion(version);
其中 11 是从一开始就存在于数据库中的版本。请注意,我没有为 ClientVersionDTO 的 name
和 code
设置值。
然后我有一个持久化新对象的服务(我使用 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();
}
}
请注意我的评论 "t2.getVersion() returns version where no values are set to 'code' and 'name'" - 我期待这样当我从数据库中获取数据时,我会使用 code
从数据库中获得一个 Version
对象并且name
值设置。但是它们没有设置。所以基本上我得到的 t2.getVersion()
对象与输入参数 translationDTO.getVersion()
中的对象相同。他们如何才能使 Version
对象重新失效?
UPDATE 尝试将@Transactional 移动到 JpaRepository,但结果仍然相同。
如果您使用的是 Hibernate,这是预期的结果。当你一个接一个地调用 translationRepository.saveAndFlush(translation)
和 translationRepository.findOne(t.getId())
时,它们会触发同一个 Hibernate 会话,该会话维护着它处理过的所有对象的缓存。因此,第二个调用简单地 returns 将对象传递给第一个。这两行中没有任何内容会强制 Hibernate 在数据库上针对 Version
实体触发 SELECT
查询。
现在,JPA 规范确实在 EntityManager
接口上有一个 refresh
方法。遗憾的是,Spring Data JPA 不使用其 JpaRepository
接口公开此方法。如果此方法可用,您可以完成 t = translationRepository.saveAndFlush(translation)
然后 versionRepository.refresh(t.getVersion())
以强制 JPA 提供程序将版本实体与数据库同步。
实现这个方法并不难。只需从 Spring Data JPA 扩展 SimpleJpaRepository
class 并自己实现该方法。有关详细信息,请参阅 adding custom behaviour to all Spring Data JPA repositories。
另一种方法是在将其设置为翻译之前将版本实体加载为 versionRepository.findOne(version.getId())
。由于您可以在代码中对版本 ID 进行硬编码,因此您的版本似乎是静态的。因此,您可以将 Version
实体标记为 @Immutable
和 @Cacheable
(前者是特定于 Hibernate 的注释)。这样,versionRepository.findOne(version.getId())
就不会在每次调用时都访问数据库。
我有一个 JpaRepository 在 Spring MVC 应用程序中保存新创建的实体。这个实体看起来像这样(非常简单):
@Entity
public class Translation {
.....
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
private Version version;
....
}
和版本实体:
@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;
}
我创建了这样一个翻译对象
TranslationDTO t = new TranslationDTO();
t.setText(translationText);
ClientVersionDTO version = new ClientVersionDTO();
version.setId(11);
t.setVersion(version);
其中 11 是从一开始就存在于数据库中的版本。请注意,我没有为 ClientVersionDTO 的 name
和 code
设置值。
然后我有一个持久化新对象的服务(我使用 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();
}
}
请注意我的评论 "t2.getVersion() returns version where no values are set to 'code' and 'name'" - 我期待这样当我从数据库中获取数据时,我会使用 code
从数据库中获得一个 Version
对象并且name
值设置。但是它们没有设置。所以基本上我得到的 t2.getVersion()
对象与输入参数 translationDTO.getVersion()
中的对象相同。他们如何才能使 Version
对象重新失效?
UPDATE 尝试将@Transactional 移动到 JpaRepository,但结果仍然相同。
如果您使用的是 Hibernate,这是预期的结果。当你一个接一个地调用 translationRepository.saveAndFlush(translation)
和 translationRepository.findOne(t.getId())
时,它们会触发同一个 Hibernate 会话,该会话维护着它处理过的所有对象的缓存。因此,第二个调用简单地 returns 将对象传递给第一个。这两行中没有任何内容会强制 Hibernate 在数据库上针对 Version
实体触发 SELECT
查询。
现在,JPA 规范确实在 EntityManager
接口上有一个 refresh
方法。遗憾的是,Spring Data JPA 不使用其 JpaRepository
接口公开此方法。如果此方法可用,您可以完成 t = translationRepository.saveAndFlush(translation)
然后 versionRepository.refresh(t.getVersion())
以强制 JPA 提供程序将版本实体与数据库同步。
实现这个方法并不难。只需从 Spring Data JPA 扩展 SimpleJpaRepository
class 并自己实现该方法。有关详细信息,请参阅 adding custom behaviour to all Spring Data JPA repositories。
另一种方法是在将其设置为翻译之前将版本实体加载为 versionRepository.findOne(version.getId())
。由于您可以在代码中对版本 ID 进行硬编码,因此您的版本似乎是静态的。因此,您可以将 Version
实体标记为 @Immutable
和 @Cacheable
(前者是特定于 Hibernate 的注释)。这样,versionRepository.findOne(version.getId())
就不会在每次调用时都访问数据库。