Реляционная модель хранит FK на стороне дочерней таблицы.
В Java у нас два способа отразить эту связь: коллекция в родительской сущности (@OneToMany / List) или ссылка в дочерней (@ManyToOne / long parentId).
Выбор между ними влияет на поведение при записи — и именно здесь большинство решений принимаются без достаточного обоснования.
Тест, который даёт однозначный ответ
Влад Михалча формулирует так: ассоциация @ManyToOne является наиболее естественным способом отображения связи «один ко многим» в базе данных и, как правило, наиболее эффективной альтернативой.
Практический критерий: если убрать коллекцию и заменить её отдельным запросом, какое бизнес-правило перестанет работать?
Если ответ — «никакое, просто список будет получаться отдельным запросом» — коллекция не нужна как часть модели.
Если ответ — «нарушится инвариант» — коллекция оправдана.
Типичные случаи:
-
дочерняя сущность не имеет смысла без родителя (value object в терминах DDD)
-
бизнес-правило требует атомарной проверки всей группы (например, максимальное количество позиций в заказе)
-
жизненный цикл дочерних объектов полностью контролируется родителем
Как это влияет на запись
Ссылка в дочерней сущности (@ManyToOne).
При записи дочерняя сущность может сохраняться независимо от родительской.
Родительский объект при этом не загружается.
Нет предварительной выборки — нет лишнего SQL.
Коллекция в родительской сущности (@OneToMany).
Здесь поведение зависит от инструмента.
JPA/Hibernate отслеживает изменения коллекции через снэпшот и генерирует необходимые INSERT/UPDATE/DELETE.
В зависимости от сценария и настроек маппинга для этого может потребоваться загрузка текущего состояния коллекции.
Дополнительно требуется корректная реализация идентификации сущностей — в частности, equals/hashCode при использовании Set и работе с detached-объектами.
Spring Data JDBC не вычисляет разницу между старым и новым состоянием дочерней коллекции.
Вместо этого дочерние элементы агрегата удаляются и создаются заново.
Это предсказуемо и просто, но означает полную перезапись при каждом save.
Для небольших агрегатов это норма, а для больших — осознанное ограничение.
JdbcClient / JdbcTemplate не управляет графом автоматически.
При выборе коллекции в родителе вы пишете логику синхронизации вручную.
При использовании ссылки в дочерней — задача тривиальна: один INSERT или UPDATE с указанием parent_id.
Практический вывод
Если бизнес-правила не требуют агрегатной согласованности, то используйте ссылку в дочерней сущности.
Это проще, дешевле при записи и не создаёт неявных зависимостей от механизмов загрузки коллекций.
Если согласованность группы объектов в одной транзакции принципиальна, то коллекция оправдана.
В этом случае выбирайте инструмент осознанно: JPA даёт точечные изменения, Spring Data JDBC полную перезапись, plain JDBC — полный контроль ценой ручной реализации.
Многие начинают моделирование с вопроса «как отобразить FK в Java».
Продуктивнее задать другой: «какие объекты должны изменяться согласованно в рамках одной транзакции».
Ответ на него часто автоматически подсказывает, нужна ли коллекция в родителе или достаточно ссылки в дочерней сущности.
Ссылки:
Влад Михалча — https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
Моя предыдущая статья по частичному чтению графов — https://habr.com/ru/articles/1044354/
ссылка на оригинал статьи https://habr.com/ru/articles/1050776/