Проверьте, есть ли ссылка на объект, чтобы предотвратить мягкое удаление без изменения базы данных.

Как видите, я использую мягкое/логическое удаление в своей системе:

Мои сущности:

@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;
}

Когда пользователь пытается удалить объект — на самом деле только логическое значение deleted переключается на true в БД, как показано выше, — я хочу проверить, объект ссылается на другой перед выполнением этого логического удаления.

Итак, если объект используется/ссылается другим, я хочу поймать это действие как ошибку или другое подобное лечение, чтобы предотвратить удаление.

Например, если Person with ID 5 прикреплен к Student, пользователь не может удалить этого человека.

Каковы лучшие методы предотвращения этого?

Важное РЕДАКТИРОВАТЬ:

1) Person упоминается в N таблицах, а не только в Student, поэтому я изучаю общий способ добиться этого (может быть, вы можете сказать hibernate проверить это?)

2) Идея состоит в том, чтобы сделать это программно, избегая модификаций БД.


person Pablo De Luca    schedule 24.04.2018    source источник
comment
Что ж, одно быстрое и грязное решение состоит в том, чтобы физически удалить сущность, сбросить контекст и снова вставить ее с deleted равным true. Из всех альтернатив это решение может иметь ощутимое снижение производительности, но оно также зависит от модели данных.   -  person crizzis    schedule 25.04.2018
comment
Согласен при условии, что модель действительно имеет ограничения внешнего ключа. Однако обычно обратимое удаление используется, когда необходимо сохранить другие записи, например старые журналы аудита, что предотвращает успешное удаление. Но если получится, то вариант интересный!   -  person ewramner    schedule 25.04.2018


Ответы (3)


Вы можете использовать проверочные ограничения на уровне базы данных. Детали специфичны для СУБД, но общая идея ограничения такова.

CHECK ((select count(*) from studens 
                         where studens.id= person.id and person.deleted=false) = 0)

Другое решение — архивная таблица

Вы можете избежать этой проблемы, удалив записи и переместив удаленные записи в таблицу person_archive (триггер базы данных может сделать это за вас). С помощью этого решения внешний ключ сможет защитить целостность данных. Если ваша таблица person большая, это решение также может быть более эффективным, потому что базе данных придется читать меньше данных. Я бы использовал таблицу person_archive, если только вам не нужно легко восстанавливать удаленные записи из пользовательского интерфейса. С удаленным флагом восстановление — это просто переключение флага. С архивной таблицей восстановление требует больше работы:

  1. выбрать из архива
  2. вставить в таблицу данных
  3. удалить из архива

Если вы не можете изменить базу данных

Если база данных не может быть изменена, эти проверки должны выполняться внутри ваших классов DAO (вам нужно вызывать запросы для всех связанных сущностей). Лучше быть уверенным, что все обращения к базе данных выполняются этими классами, иначе (если кто-то использует прямой SQL) вы можете получить инвариант базы данных, который не будет истинным.

person Bartosz Bilicki    schedule 24.04.2018
comment
Ваш подход хорош, но, поскольку я только что отредактировал, я не могу коснуться БД - person Pablo De Luca; 24.04.2018
comment
«не могу прикоснуться к БД». Это прискорбно (рискованно и хрупко), потому что кто-то сможет разорвать контракт при прямом использовании SQL или из других приложений. - person Bartosz Bilicki; 24.04.2018
comment
Да, я знаю, что лучше всего это делать на БД, но таковы директивы. - person Pablo De Luca; 24.04.2018

Вы должны проверить эту ссылку с помощью запроса к базе данных.

Например, Если есть таблица Person и Student. Если вы удаляете запись в таблице лиц с идентификатором = 5, вы можете проверить первую ссылку в таблице учеников, используя запрос, например

select count(1) from student where person_id = 5

Если результат > 0, то это ошибка в вашем случае, в противном случае продолжите удаление

person Pranav Maniar    schedule 24.04.2018
comment
Как я только что отредактировал, Person ссылается на N таблиц. Но ваш подход хорош для моего вопроса перед этим изданием. - person Pablo De Luca; 24.04.2018

Лучше всего делать это в базе данных, без аргументов. Ссылочная целостность, поддерживаемая в коде приложения, всегда в какой-то момент нарушается, хотя бы потому, что люди склонны обращаться к таблицам из нескольких приложений и вручную.

Программно вы можете проверить, создав запрос, который проверяет, существует ли что-то, что ссылается на человека. Я бы использовал JPA QL или HQL или даже критерии, не нужно переходить к SQL. Одного запроса должно быть достаточно (в сочетании с ИЛИ). Хитрость заключается в том, чтобы найти все сущности и поля для включения в запрос. В одном случае это просто, создайте запрос вручную. Вы, вероятно, знаете, что ссылается на Person, если вы этого не сделаете, вы можете выполнить поиск в среде IDE или проверить ограничения в базе данных. В общем случае вы хотите сгенерировать этот код или создать его на лету.

Для построения «на лету» EntityManager.getMetamodel() предоставит вам все объекты, чтобы вы могли просмотреть их атрибуты и посмотреть, есть ли потенциальные ссылки на объект, с которым вы работаете. Громоздкий.

Генерация кода на основе аннотированных классов может быть сложной, но если вы знаете, какие классы проверять (и если вы перечисляете их в своем блоке постоянства, вы это делаете), это становится намного проще. Пройдите перечисленные классы, прочитайте аннотации и сгенерируйте код проверки.

Если вы действительно полны решимости сделать это программно, я бы начал с создания первых двух реализаций вручную. Затем я бы использовал это как основу для сгенерированного кода.

person ewramner    schedule 24.04.2018