JpaRepository缓存新创建的对象.如何刷新?

Ser*_*nov 24 java jpa spring-data-jpa

我有一个JpaRepository在Spring MVC应用程序中持久保存新创建的实体.这个实体看起来像这样(非常简化):

@Entity
public class Translation {

    .....

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Version version;

    ....

}
Run Code Online (Sandbox Code Playgroud)

和版本实体:

@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;

}
Run Code Online (Sandbox Code Playgroud)

我创建了这样的翻译对象

            TranslationDTO t = new TranslationDTO();
            t.setText(translationText);
            ClientVersionDTO version = new ClientVersionDTO();
            version.setId(11);
            t.setVersion(version);
Run Code Online (Sandbox Code Playgroud)

其中11是从一开始就存在于数据库中的版本.请注意,我不为她设定值namecodeClientVersionDTO的.

然后我有一个持久存在新对象的服务(我使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意我的评论"t2.getVersion()返回版本,其中没有值设置为'code'和'name'" - 我当时希望这样当我从数据库中获取数据时,我会Version直接从数据库中获取一个对象与codename值设置.但是他们没有设定.所以基本上我作为一个t2.getVersion()对象得到的是与输入参数相同的对象translationDTO.getVersion().他们怎么能重新使Version对象失效?

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

man*_*ish 37

如果您正在使用Hibernate,这是预期的结果.当你打电话translationRepository.saveAndFlush(translation),并translationRepository.findOne(t.getId())一前一后,他们打它认为它一直对所有对象的缓存同一个Hibernate Session.因此,第二个调用只返回传递给第一个的对象.这两行中没有任何内容可以强制Hibernate SELECTVersion实体的数据库上触发查询.

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

实现这种方法并不困难.只需SimpleJpaRepository从Spring Data JPA 扩展类并自己实现该方法.有关详细信息,请参阅向所有Spring Data JPA存储库添加自定义行为.

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