【问题标题】:JpaRepository caches newly created object. How to refresh it?JpaRepository 缓存新创建的对象。如何刷新它?
【发布时间】:2015-03-24 16:03:21
【问题描述】:

我有一个 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 的 namecode 设置值。

然后我有一个持久化新对象的服务(我使用 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() 返回没有值设置为 'code' 和 'name' 的版本” - 我期待这样当我从数据库中获取数据时,我会得到一个 @987654329来自数据库的 @ 对象,设置了 codename 值。但是它们没有设置。所以基本上我得到的t2.getVersion() 对象与输入参数translationDTO.getVersion() 中的对象相同。我怎样才能使Version 对象重新失效?

UPDATE 尝试将 @Transactional 移动到 JpaRepository,但结果仍然相同。

【问题讨论】:

  • 您可能是指“Spring MVC”吗?那是因为你写了“Sprint MVC”
  • 好的,谢谢。修改错别字。
  • 尝试将您的@Transactional 移动到您的存储库进行测试
  • benjamin.d,刚刚尝试过,但没有成功。
  • 找到更好的解决方案了吗?我也有类似的问题:stackoverflow.com/questions/45491551/…

标签: java jpa spring-data-jpa


【解决方案1】:

如果您使用的是 Hibernate,这是预期的结果。当您一个接一个地调用translationRepository.saveAndFlush(translation)translationRepository.findOne(t.getId()) 时,它们会访问同一个Hibernate 会话,该会话维护它处理过的所有对象的缓存。因此,第二个调用只是返回传递给第一个的对象。这两行中没有任何内容会迫使 Hibernate 在数据库上为 Version 实体触发 SELECT 查询。

现在,JPA 规范在 EntityManager 接口上确实有一个 refresh 方法。不幸的是,Spring Data JPA 没有使用其JpaRepository 接口公开此方法。如果此方法可用,您可以先执行t = translationRepository.saveAndFlush(translation),然后执行versionRepository.refresh(t.getVersion()) 来强制 JPA 提供程序将版本实体与数据库同步。

实施这种方法并不困难。只需从 Spring Data JPA 扩展 SimpleJpaRepository 类并自己实现该方法。详情见adding custom behaviour to all Spring Data JPA repositories

另一种方法是将版本实体加载为versionRepository.findOne(version.getId()),然后再将其设置为翻译。由于您可以在代码中硬编码版本 ID,因此您的版本似乎是静态的。因此,您可以将Version 实体标记为@Immutable@Cacheable(前者是特定于Hibernate 的注释)。这样,versionRepository.findOne(version.getId()) 就不会在每次调用时都命中数据库。

【讨论】:

  • 谢谢,确实我也得出结论并使用了第二种方法。顺便说一句,版本没有硬编码,只是为了简单起见。
猜你喜欢
  • 1970-01-01
  • 2020-02-12
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 2018-11-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多