У меня есть следующий код:
У меня есть однонаправленное отношение один-ко-многим между Article и Comments:
@Entity
public class Article {
@OneToMany(orphanRemoval=true)
@JoinColumn(name = "article_id")
private List<Comment> comments= new ArrayList<>();
…
}
Я использовал set ophanRemoval=true
, чтобы отметить «дочерняя» сущность должна быть удалена, когда на нее больше нет ссылок из «родительской» сущности, например, когда вы удаляете дочернюю сущность из соответствующей коллекции родительской сущности.
Вот пример:
@Service
public class MyService {
public Article modifyComment(Long articleId) {
Article article = repository.findById(articleId);
List<Comments> comments = article.getComments();
//Calls a method which modifies removes some comments from the collection based on some logic
removeSomeComments(comments); //side effect
modifyComments(comments); //side effect
.....
return repository.save(article);
}
}
Итак, у меня есть несколько операторов, которые выполняют некоторые действия с коллекцией, которые затем сохраняются в базе данных. В приведенном выше примере я получаю статью из базы данных, выполняю некоторые мутации на объекте, удаляя / изменяя некоторые комментарии, а затем сохраняя ее в базе данных.
Я не уверен, каков самый чистый способ изменения коллекций объектов без большого количества побочных эффектов, что приводит к подверженному ошибкам коду (мой код более сложен и требует многократных мутаций в коллекции).
Поскольку я нахожусь внутри транзакции, любые изменения (добавление, удаление или изменение дочерних элементов) в коллекции будут сохраняться при следующем вызове EntityManager.commit()
.
Однако я попытался реорганизовать этот код и написать его в более выразительном функциональном стиле:
public Article modifyComment(Long articleId) {
Article article = repository.findById(articleId);
List<Comment> updatedComments = article.getComments().stream()
filter(some logic..) //remove some comments from the list based on a filter
sorted()
.filter(again some logic) //do more stuff
.collect(Collectors.toList());
article.add(updatedComments);
return repository.save(article);
}
Мне больше нравится этот подход, так как он короткий, лаконичный и более выразительный. Однако это не будет работать, так как выдает: A collection with cascade=“all-delete-orphan” was no longer referenced by the owning entity instance
Это потому, что я назначаю новый список (updatedComments
). Если я хочу удалить или изменить дочерние элементы из родительского, я должен изменить содержимое списка, а не назначать новый список.
Поэтому мне пришлось сделать это в конце:
article.getComments().clear();
article.getComments().addAll(updatedComments);
repository.save(article)
Считаете ли вы второй пример хорошей практикой?
Я не уверен, как работать с коллекциями в JPA. Моя бизнес-логика c более сложная, и я хочу избежать 3-4 методов, которые изменяют данную коллекцию (присоединенную к сеансу гибернации), которая была передана как параметр.
Я думаю, что во втором примере есть меньший потенциал для побочных эффектов, потому что он не изменяет любой входной параметр. Как вы думаете?
(я использую Spring-Boot 2.2.5)