2015-01-26 2 views
15

У меня есть JpaRepository, сохраняющийся вновь созданный объект в приложении Spring MVC. Эта структура выглядит следующим образом (очень упрощенный):JpaRepository кэширует новый объект. Как его обновить?

@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 является версия, которая существует в база данных уже с самого начала. Обратите внимание, что я не устанавливаю значения для 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 '"- Я ожидал, что, когда я извлечу данные из базы данных, я бы получил объект Version прямо из базы данных с установленными значениями code и name. Однако они не установлены. Таким образом, в основном то, что я получаю как объект t2.getVersion(), это тот же объект, что и входной аргумент translationDTO.getVersion(). Как они могут повторно аннулировать объект Version?

UPDATE попытался переместить @Transactional в JpaRepository, но все тот же результат.

+0

Возможно, вы имеете в виду «Spring MVC»? Это потому, что вы написали «Sprint MVC» – AlexR

+0

Да, спасибо. Изменены опечатки. –

+0

Попробуйте переместить @Transactional в свой репозиторий для теста –

ответ

26

Если вы используете 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 и реализуйте метод самостоятельно. Подробнее см. adding custom behaviour to all Spring Data JPA repositories.

Альтернативой было бы загрузить объект версии как versionRepository.findOne(version.getId()), прежде чем устанавливать его на перевод. Поскольку в коде вы можете указать код версии с жестким кодом, ваши версии кажутся статическими. Таким образом, вы можете пометить свой объект Version как @Immutable и @Cacheable (первый - аннотация для гиберната). Таким образом, versionRepository.findOne(version.getId()) не должен попадать в базу данных каждый раз, когда он вызывается.

+0

спасибо, действительно, я также пришел к выводу и использовал второй подход. Btw, версия не кодируется, она здесь просто для простоты. –

Смежные вопросы