Платформа предоставляет средство обработки связей при удалении сущностей, во многом аналогичное правилам 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.