Общая проблема
У нас есть сущности, Родитель и Дочерний элемент, с отношениями один-ко-многим между ними. При обновлении родителя мы хотим автоматически обновлять некоторые записи во всех связанных дочерних элементах.
Для простоты мы также сохраняем измененное свойство (значение) в дочернем объекте.
Основная проблема заключается в изменении различных свойств и расчете нового свойства. у родственных детей на основе этих изменений.
Getter/Setter ToString... опущены для простоты.
@Entity общественный класс ParentEntity { @Идентификатор @GeneratedValue частный идентификатор UUID; частное имя строки; частное строковое значение; @OneToMany(mappedBy = "родительский", cascade = CascadeType.ALL) частный List дети; } @Сущность общественный класс ChildEntity { @Идентификатор @GeneratedValue частный идентификатор UUID; @ManyToOne @JoinColumn(name="parent_id") частный родитель ParentEntity; частное строковое значение; } Используемый подход — прослушиватель событий Поскольку изменения вносятся с помощью разных сервисов и методов, мы ищем централизованное решение, поэтому мы решили использовать Event-Listeners в родителе, который запускает процесс обновления во всех его дочерних элементах.
Показаны только дополнительные методы
публичный класс ParentEntity { @PrePersist @PreUpdate недействительный onUpdate() { если (дети != ноль) { this.children.forEach(ChildEntity::update) } } } общественный класс ChildEntity { недействительное обновление () { если (родительский != ноль) { this.value = родительское.getValue(); } } } Проблема Этот подход не всегда обновляет дочерний объект при изменении значения родительского объекта.
Мы выявили множество факторов, влияющих на результат
[*]Аннотация @OneToMany для ParentEntity#children с нетерпеливым/ленивым извлечением [*]Аннотация @Fetch для ParentEntity#children, специфичная для Hibernate, с JOIN/SELECT/SUBSELECT [*]Доступ к дочернему списку в рамках той же транзакции, что и обновление. [*]Специально для весны: используйте findById репозитория или собственный findByName
Мы также пробовали использовать аннотации @EntityGraph, но отказались от этого подхода. поскольку это будет зависеть от доступа, что сводит на нет подход «одного метода».
вызов метода сохранения хранилища внутри транзакции не имеет никакого эффекта.
Это приводит к следующей матрице успеха/неуспеха
Результаты успешные: + неудачные: -
Дочерний список, помеченный режимом @OneToMan fetch LAZY и @Fetch
без аннотаций ПРИСОЕДИНИТЬ ВЫБРАТЬ или ПОДВЫБОР найти по идентификатору - + - найти по идентификатору и доступу + + + без аннотаций ПРИСОЕДИНИТЬ ВЫБРАТЬ или ПОДВЫБОР найти по имени - + - найти по имени и доступу + + +
Дочерний список, аннотированный с помощью @OneToMan fetch EAGER и режима @Fetch
без аннотаций ПРИСОЕДИНИТЬ ВЫБРАТЬ или ПОДВЫБОР найти по идентификатору - - - найти по идентификатору и доступу - - + без аннотаций ПРИСОЕДИНИТЬ ВЫБРАТЬ или ПОДВЫБОР найти по имени + + - найти по имени и доступу + + +
Небольшое примечание: я не мог опубликовать поиск по идентификатору и поиск по имени как одну таблицу — stackoverflow не позволил бы это сделать
Открытые вопросы [*]Почему вызовы репозитория по findById и findByName дают разные результаты? [*]Какую комбинацию аннотаций использовать? [*]Есть ли совершенно другой подход?
Исходный код и некоторые SpringBootTests с дополнительным выводом журнала можно найти на GitHub
Обновить связанную сущность из обратного вызова прослушивателя событий ⇐ JAVA
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение