JPA + Hibernate - Циклы в отношениях сущностей - Каскадная стратегия

У меня есть набор сущностей, которые соединяются друг с другом, образуя цикл, т.е. родительский объект P имеет два отношения "один ко многим" с двумя дочерними объектами C1 и C2, и каждый из них имеет отношение "один ко многим" с другим объектом A. Объект A реализует ассоциацию этих объектов (C1, C2) и определяет атрибуты отношений (это не только таблица соединений). Все отношения являются судоходными в обоих направлениях.
domain objects
Из этого дизайна возникает следующий вопрос: какова должна быть стратегия каскада, чтобы сущность A могла быть сохранена/объединена, учитывая, что вы всегда вызываете операции менеджера сущностей в корневой объект P? Должен ли A быть каскадом, доступным с обоих путей?

Соображения: Похоже, что если приложение решит предоставить только один каскадный путь, могут возникнуть сценарии, которые вызывают исключение TransientObjectException. Если он предоставляет оба пути, то эти пути должны выполнить полный цикл, так как, например, C1 можно было бы попытаться сохранить через A.

Версии: JPA 2.0, Hibernate core 4.1.7, hibernate-jpa-2.0-api 1.0.1

Ответ 1

Я могу дать вам свои 2 цента, извините, если мой ответ немного длинный.

Если у вас есть каскадный конфликт такого рода, возможно, это связано с тем, что ваш каскадный подход или модель домена не определены правильно. Я бы постарался обобщить каскадную стратегию на общий график или несвязанный набор элементов.

Мой совет будет заключаться в том, что каскадную стратегию следует использовать только для набора данных, тесно связанного вместе, и того же типа, что и для класса и его (частных) внутренних классов в java-мире. Связь между материнским классом и его детьми также должна быть исключительной (в UML это называется ассоциацией без разделения).

Конечно, вы можете сделать иначе (все мы можем быть ленивыми), но в конце вы можете создать сеть связи между вашим единственным потоком непрерывности (или конфигурацией сохранения) и вашим бизнес-потоком. Вам нужно будет управлять множеством исключений и выполнять большую логику конфигурации вокруг ранее созданной стратегии каскада (сохранить, обновить, удалить).

Крайним подходом было бы то, что некоторые могут захотеть сохранить только один большой корневой объект. Почему нет? остальное "должно сохраняться в каскаде". Но на самом деле это может серьезно повлиять на ремонтопригодность вашей системы. Кроме того, вам может потребоваться управлять состоянием вашего большого графика в памяти, при загрузке, сохранении и объединении.

Если вы используете веб-приложение или любое клиент-серверное приложение, ваш веб-рабочий процесс должен иметь возможность сохранять ограниченный набор объектов при каждом запросе без необходимости сохранять все из корневого элемента. Я знаю, что не отвечаю прямо на ваш вопрос. Итак, вернемся к вашему примеру:

Предположим, что P - это банк, а C1 и C2 - два Клиента, а A - Продукт.

У меня есть два простых ответа: 1) Каждый слой может быть сохранен отдельно без каскадирования. Но это может быть сделано внутри одной и той же транзакции и в том же DAO или нет, если вы хотите.

2) P и C "могут быть каскадированы. Но A должен быть сохранен в другом рабочем процессе.

Это напоминает мне о главе Питера Коада, где он рассказывает о "анализе, управляемом доменом": http://www.petercoad.com/download/bookpdfs/jmcuch01.pdf

В этой главе объясняется, как различные объекты в графе могут быть разделены в разных архетипах. Рабочий процесс сохранения не должен совпадать между транзакционными данными и описанием, или "вещью". Это может помочь создать лучшую каскадную стратегию:

 The four archetypes of Peter Coad are:
 - Is it a moment or interval? 
 - Is it a role played? 
 - Is it a catalog-entry-like description? 
 - Otherwise, it a party, place, or thing.

Надеюсь, это поможет.

Ответ 2

В целом хорошая идея состоит в том, чтобы каскадировать только в тесных ассоциациях и только в parent- > child (или владельце- > собственном) направлении. В вашем случае это будет, вероятно, P->C1 и P->C2. Поскольку A не имеет одного очевидного родителя, его следует сохранять отдельно. Это можно сделать как @etienno, упомянутое в вашем DAO, в одной транзакции вместе с PC1, C2). Я не знаю вашу модель домена, но, возможно, даже A на концептуальном уровне представляет собой отдельный объект, и в этом случае отдельное сохранение еще более оправдано.

Каскадирование многих объектов, которые не связаны друг с другом, может в долгосрочной перспективе сделать весь график, когда он станет больше, неуправляемым.

В подобных ситуациях всегда хорошо задавать вопрос: "Как бы вы это сделали без Hibernate". В вашем случае вы, вероятно, сначала сохраните P, затем C1, C2, а затем A. AFAIK JPA/Hibernate не предоставляет какого-либо явного способа принудительного исполнения такого заказа, поэтому некоторые вещи должны выполняться вами вручную.