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