5.2.1.4.2. Политика обработки связей
Платформа предоставляет средство обработки связей при удалении сущностей, во многом аналогичное правилам ON DELETE внешних ключей в базе данных. Это средство работает на уровне Middleware и использует аннотации @OnDelete, @OnDeleteInverse атрибутов сущности.
Аннотация @OnDelete
обрабатывается при удалении той сущности, в которой она встретилась, а не той, на которую указывает аннотированный атрибут (в этом отличие от каскадных удалений на уровне БД).
Аннотация @OnDeleteInverse
обрабатывается при удалении той сущности, на которую указывает аннотированный атрибут, (т.е. аналогично каскадному удалению на уровне внешних ключей в БД). Эта аннотация полезна при отсутствии в удаляемом объекте атрибута, который нужно проверять при удалении. При этом, как правило, в проверяемом объекте существует ссылка на удаляемый, на этот атрибут и устанавливается аннотация @OnDeleteInverse
.
Значением аннотации может быть:
-
DeletePolicy.DENY
- запретить удаление сущности, если аннотированный атрибут неnull
или не пустая коллекция -
DeletePolicy.CASCADE
- каскадно удалить аннотированный атрибут -
DeletePolicy.UNLINK
- разорвать связь с аннотированным атрибутом. Разрыв связи имеет смысл указывать только на ведущей стороне ассоциации - той, которая в классе сущности аннотирована@JoinColumn
.
Примеры:
-
Запрет удаления при наличии ссылки: при попытке удаления экземпляра
Customer
, на который ссылается хотя бы одинOrder
, будет выброшено исключениеDeletePolicyException
.Order.java
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "CUSTOMER_ID") @OnDeleteInverse(DeletePolicy.DENY) protected Customer customer;
Customer.java
@OneToMany(mappedBy = "customer") protected List<Order> orders;
Сообщения в окне исключения могут быть локализованы в главном пакете сообщений. Используйте для этого следующие ключи:
-
deletePolicy.caption
- заголовок уведомления. -
deletePolicy.references.message
- тело сообщения. -
deletePolicy.caption.sales$Customer
- заголовок уведомления для конкретной сущности. -
deletePolicy.references.message.sales$Customer
- тело сообщения для конкретной сущности.
-
-
Каскадное удаление элементов коллекции: при удалении экземпляра
Role
все экземплярыPermission
также будут удалены.Role.java
@OneToMany(mappedBy = "role") @OnDelete(DeletePolicy.CASCADE) protected Set<Permission> permissions;
Permission.java
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ROLE_ID") protected Role role;
-
Разрыв связи с элементами коллекции: удаление экземпляра
Role
приведет к установке вnull
ссылок со стороны всех входивших в коллекцию экземпляровPermission
.Role.java
@OneToMany(mappedBy = "role") protected Set<Permission> permissions;
Permission.java
@ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "ROLE_ID") @OnDeleteInverse(DeletePolicy.UNLINK) protected Role role;
Особенности реализации:
-
Политика обработки связей реализуется при сохранении данных в БД на уровне Middleware.
-
Нужно быть осторожным при использовании
@OnDeleteInverse
с политикамиCASCADE
иUNLINK
, так как при этом происходит извлечение из БД на сервер приложения всех экземпляров ссылающихся объектов, изменение и затем сохранение.Например, в случае ассоциации
Customer
-Job
и большого количества работ для одного заказчика, если поставить на атрибутJob.customer
политику@OnDeleteInverse(CASCADE)
, то при удалении экземпляра заказчика будет предпринята попытка извлечь и изменить все его работы. Это может привести к перегрузке сервера приложения и БД.С другой стороны, использование
@OnDeleteInverse(DENY)
безопасно, так как при этом производится только подсчет количества ссылающихся объектов, и если оно больше0
, выбрасывается исключение. Поэтому@OnDeleteInverse(DENY)
для атрибутаJob.customer
вполне допустимо. -
Политика
UNLINK
не поддерживается для ссылок на коллекции с отношениями one-to-many и many-to-many: при попытке удаления экземпляра сущности на ведущей стороне ассоциации будет выброшено исключениеUnsupportedOperationException
. Пример ошибочной политики:Owner.java
@JoinTable(name = "SAMPLE_OWNER_SUBORDINATE_LINK", joinColumns = @JoinColumn(name = "OWNER_ID"), inverseJoinColumns = @JoinColumn(name = "SUBORDINATE_ID")) @OnDelete(DeletePolicy.UNLINK) @ManyToMany protected List<Subordinate> subordinate;