JpaRepository кэширует вновь созданный объект. Как его обновить?
У меня есть JpaRepository, сохраняющийся вновь созданный объект в приложении Spring MVC. Этот объект выглядит так (очень упрощен):
@Entity
public class Translation {
.....
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@ManyToOne(fetch = FetchType.LAZY)
private Version 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 - это версия, которая существует в базе данных уже с самого начала. Обратите внимание, что я не устанавливаю значения для name
и code
ClientVersionDTO.
Затем у меня есть служба, которая сохраняет новый объект (я использую библиотеку 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 ". - Я ожидал, что когда я извлечу данные из базы данных, я получаю a Version
прямо из базы данных с установленными значениями code
и name
. Однако они не установлены. Таким образом, в основном то, что я получаю как объект t2.getVersion()
, - это тот же объект, что и входной аргумент translationDTO.getVersion()
. Как они могут повторно аннулировать объект Version
?
UPDATE попытался переместить @Transactional в JpaRepository, но все тот же результат.
Ответы
Ответ 1
Если вы используете Hibernate, это ожидаемый результат. Когда вы вызываете translationRepository.saveAndFlush(translation)
и translationRepository.findOne(t.getId())
один за другим, они попадают в тот же сеанс Hibernate, который поддерживает кеш всех объектов, над которыми он работал. Поэтому второй вызов просто возвращает объект, переданный в первый. В этих двух строках нет ничего, что вынудило Hibernate запускать запрос SELECT
в базе данных для объекта Version
.
Теперь спецификация JPA имеет метод refresh
на интерфейсе EntityManager
. К сожалению, Spring Data JPA не предоставляет этот метод, используя его интерфейс JpaRepository
. Если бы этот метод был доступен, вы могли бы сделать t = translationRepository.saveAndFlush(translation)
, а затем versionRepository.refresh(t.getVersion())
, чтобы заставить поставщика JPA синхронизировать объект версии с базой данных.
Реализация этого метода не составляет труда. Просто добавьте класс SimpleJpaRepository
из Spring Data JPA и реализуйте метод самостоятельно. Подробнее см. добавление пользовательского поведения ко всем хранилищам данных Spring Data JPA.
Альтернативой было бы загрузить объект версии как versionRepository.findOne(version.getId())
, прежде чем устанавливать его на перевод. Поскольку в коде вы можете указать код версии с жестким кодом, ваши версии кажутся статическими. Поэтому вы можете пометить свой объект Version
как @Immutable
и @Cacheable
(первый - аннотация для гиберната). Таким образом, versionRepository.findOne(version.getId())
не должен попадать в базу данных каждый раз, когда он вызывается.