Какие проблемы возникают при использовании коллекции с отношением OneToMany в JPA
Что проверяют
Понимаете ли вы, какой SQL рождает маппинг коллекции и кто в связи владелец. На @OneToMany видно, работал ли человек с Hibernate под нагрузкой или только по туториалам. Следом обычно просят написать правильный двунаправленный маппинг.
Как отвечать
Основные проблемы:
- однонаправленный
@OneToManyбез@JoinColumn: Hibernate заводит промежуточную таблицу, как для связи многие ко многим. С@JoinColumnтаблицы нет, но внешний ключ проставляется отдельнымUPDATEпосле вставки; - владелец связи: в двунаправленной связи владеет сторона
@ManyToOne, а коллекция помечаетсяmappedBy. Изменение только коллекции в базу не попадёт, нужны методыaddChildиremoveChild, которые синхронизируют обе стороны; - N+1: коллекция ленивая по умолчанию, и обход списка родителей с обращением к детям даёт запрос на каждого. Лечится
JOIN FETCH,@EntityGraph,@BatchSize; LazyInitializationException: обращение к коллекции после закрытия сессии. Решение в загрузке нужного внутри транзакции, а не вEAGERи не вspring.jpa.open-in-view, который в Spring Boot включён по умолчанию и держит соединение до конца HTTP-запроса;- большие коллекции: коллекцию нельзя пагинировать,
getChildren()поднимет все десятки тысяч строк. Такие связи лучше не маппить, а читать через репозиторий постранично; equalsиhashCodeвSet: если они построены на сгенерированномid, послеpersistхэш меняется и элемент теряется внутриHashSet. Нужен натуральный ключ или постоянныйhashCode;- каскады:
CascadeType.ALLиorphanRemoval = trueуместны, только когда дети не живут без родителя.
Где спотыкаются
Советуют EAGER против ленивых исключений. Это меняет одну ошибку на N+1 и лишние джойны везде.
Путают orphanRemoval и CascadeType.REMOVE. Каскад удаляет детей вместе с родителем, а orphanRemoval ещё и удаляет ребёнка, которого просто убрали из коллекции.
Считают, что Set вместо List экономит память. Настоящая причина другая: при JOIN FETCH двух коллекций типа List Hibernate бросает MultipleBagFetchException, а Set снимает запрет, хотя декартово произведение остаётся.