Как видите, в моей системе я использую мягкое / логическое удаление:
Мои сущности:
@Where(clause = "deleted='false'")
public class Person {
//...
}
Мои услуги:
@Override
public ServiceResponse DeletePerson (Person entity) {
ServiceResponse sr = new ServiceResponse<>();
try {
sr = ValidateDeletePerson(entity); //Business logic treatment
if (sr.hasError())
return sr;
Person dbEntity = GetPerson(entity.getPersonID());
dbEntity.setDeleted(true);
_repository.save(dbEntity);
} catch (Exception ex) {
sr.getErrors().add(new ServiceError(ex));
}
return sr;
}
Когда пользователь пытается удалить объект - на самом деле только логическое удалено переключается на правда в БД, как я демонстрирую выше, - я хочу, чтобы проверьте, есть ли ссылка на объект другому перед выполнял это логическое удаление.
Итак, если объект используется / на который ссылается другой, я хочу перехватить это действие как ошибку или как другую подобную обработку, чтобы предотвратить удаление.
Пример. Если Person with ID 5 подключен к Student, пользователь не может удалить этого человека.
Что такое "лучшая практика", чтобы предотвратить это?
Важное РЕДАКТИРОВАНИЕ:
1) На человека есть ссылки в N таблицах, не только для Student, поэтому я исследую общий способ достижения этого (может быть, вы можете указать спящему режиму, чтобы проверить это?)
2) По идее сделать программно, избегая модификаций в БД.
Согласовано при условии, что модель действительно имеет ограничения внешнего ключа. Однако обычно мягкое удаление используется, когда необходимо сохранить другие записи, например старые журналы аудита, что предотвращает успешное реальное удаление. Но если это сработает, это интересный вариант!




Вы можете использовать проверить ограничения на уровне базы данных. Детали специфичны для СУБД, но общая идея ограничения такова:
CHECK ((select count(*) from studens
where studens.id= person.id and person.deleted=false) = 0)
Другое решение - архивный стол
Вы можете избежать этой проблемы, удалив записи и переместив удаленные записи в таблицу person_archive (триггер базы данных может сделать это за вас). С помощью этого решения внешний ключ сможет защитить целостность данных. Если ваша таблица person большая, это решение также может быть более эффективным, потому что базе данных придется читать меньше данных.
Я бы использовал таблицу person_archive, если вам не нужно легко восстанавливать удаленные записи из пользовательского интерфейса. С удаленным флагом восстановление - это просто перекидной флаг. С архивной таблицей восстановление требует больше усилий:
Если вы не можете изменить базу данных
Если база данных не может быть изменена, эти проверки должны выполняться внутри ваших классов DAO (вам необходимо вызывать запросы для всех связанных сущностей). Лучше быть уверенным, что все обращения к базе данных осуществляются этими классами, иначе (если кто-то использует прямой SQL) вы можете получить инвариант базы данных, не поддерживающий истину.
Ваш подход хорош, но, поскольку я только что отредактировал, я не могу прикоснуться к БД
"не могу коснуться БД". Это прискорбно (рискованно и хрупко), потому что кто-то сможет разорвать контракт при использовании SQL напрямую или из других приложений.
Да, я знаю, что лучше всего это делать в БД, но это директивы
Вы должны проверить эту ссылку с помощью запроса к базе данных.
Например, Если есть таблица Person и Student. Если вы удаляете запись в таблице людей с ID = 5, вы можете проверить первую ссылку в таблице учеников, используя запрос типа
select count(1) from student where person_id = 5
Если результат> 0, то это ошибка в вашем случае, в противном случае продолжайте удаление
Как я только что редактировал, на Person есть ссылки в N таблицах. Но ваш подход хорош для моего вопроса перед этим выпуском.
Лучше всего делать это в базе данных, никаких споров по этому поводу. Ссылочная целостность, обрабатываемая в коде приложения, всегда в какой-то момент нарушается, хотя бы потому, что люди имеют тенденцию обращаться к таблицам из нескольких приложений и вручную.
Программно вы можете проверить, построив запрос, который проверяет, существует ли что-то, что ссылается на человека. Я бы использовал JPA QL или HQL или даже критерии, нет необходимости опускаться до SQL. Одного запроса должно хватить (объединить с ИЛИ). Уловка состоит в том, чтобы найти все объекты и поля, которые нужно включить в запрос. В одном случае это просто, вручную создайте запрос. Вы, вероятно, знаете, что ссылается на Person, если нет, вы можете выполнить поиск в среде IDE или проверить ограничения в базе данных. В общем случае вы хотите сгенерировать этот код или построить его на лету.
Для создания на лету EntityManager.getMetamodel() предоставит вам все сущности, чтобы вы могли просмотреть их атрибуты и посмотреть, есть ли потенциальные ссылки на сущность, с которой вы работаете. Громоздкий.
Генерация кода на основе аннотированных классов может быть сложной задачей, но если вы знаете, какие классы проверять (и если вы перечисляете их в своем блоке постоянства, вы это делаете), это становится намного проще. Просмотрите перечисленные классы, прочтите аннотации и сгенерируйте код проверки.
Если вы действительно полны решимости сделать это программным способом в целом, я бы начал с ручной работы с первыми двумя реализациями. Затем я бы использовал это как основу для сгенерированного кода.
Что ж, одно быстрое и грязное решение - физически удалить объект, очистить контекст и снова вставить его с
deleted, равнымtrue. Из всех альтернатив это решение может действительно иметь существенное снижение производительности, но оно также зависит от модели данных.