3.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 при сохранении сущностей, реализующих интерфейс
SoftDelete, в БД. -
Нужно быть осторожным при использовании
@OnDeleteInverseс политикамиCASCADEиUNLINK, так как при этом происходит извлечение из БД на сервер приложения всех экземпляров ссылающихся объектов, изменение и затем сохранение.Например, в случае ассоциации
Customer-Jobи большого количества работ для одного заказчика, если поставить на атрибутJob.customerполитику@OnDeleteInverse(CASCADE), то при удалении экземпляра заказчика будет предпринята попытка извлечь и изменить все его работы. Это может привести к перегрузке сервера приложения и БД.С другой стороны, использование
@OnDeleteInverse(DENY)безопасно, так как при этом производится только подсчет количества ссылающихся объектов, и если оно больше0, выбрасывается исключение. Поэтому@OnDeleteInverse(DENY)для атрибутаJob.customerвполне допустимо.