Hibernate Enver не проверяет поле, которое используется при мягком удалении? - PullRequest
0 голосов
/ 09 мая 2018

Я использую настройки hibernate.envers:

properties.put("org.hibernate.envers.audit_table_suffix", "_HISTORY");
properties.put("org.hibernate.envers.store_data_at_delete", "true");

Основная таблица, как это:

@Entity
@Table(name = "person")
@SQLDelete(sql = "UPDATE person SET deleted = NOW() WHERE id = ?", check = ResultCheckStyle.COUNT)
@Where(clause = "deleted is NULL")
@Audited
public class Person {
  @Id
  private UUID id;

  private OffsetDateTime deleted;

  //omit other fields
}

Когда происходит событие удаления, таблица Person_HISTORY будет хранить все поля Person, кроме поля deleted. (deleted поле в таблице Person_HISTORY было пустым)

Есть идеи, как сохранить поле deleted в Person_HISTORY при удалении события?

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Приятно знать решение от @Naros, у нас фактически возникла та же проблема с этим, вот наше обходное решение с изменением кода на службе

@Transactional
public void deletePerson(UUID id) {
    PersonEntity personEntity= getPerson(id);
    personEntity.setDeleted(OffsetDateTime.now());
    personRepository.flush();
    personRepository.delete(personEntity);
}

Есть два основных шага, на которых мы должны сосредоточиться:

  • Должен работать на той же транзакции (тогда мы пометили @Transactional)
  • И flush это.

Не приятно, но работает: D, PostDeleteEvent придет с deleted данными

0 голосов
/ 09 мая 2018

Проще говоря, модификация deleted не включена в PostDeleteEvent.

Hibernate создает состояние, которое включается в PostDeleteEvent один раз, в начале рабочего процесса удаления сущности. Для традиционного использования это имеет смысл, поскольку выполняемый оператор является DELETE, поэтому строка не изменяется.

Загадка здесь - @SQLDelete просто заменяет сгенерированный оператор DELETE, который персистент использует для использования, на собственную версию. Вообще говоря, предоставляемый вами SQL может быть любым, если он является синтаксически верным DML.

Что Hibernate не знает, так это то, что в данном случае SQL для замены является UPDATE. Следовательно, повторная синхронизация состояния моментального снимка персистера не происходит, и состояние события не изменяется после того, как персистер выполнил оператор удаления SQL, а когда срабатывает обратный вызов с удалением post , им все еще предоставляется состояние в начале рабочего процесса удаления объекта.

С учетом сказанного, Энверс записывает REVTYPE=2 (он же DEL) в таблицу аудита. Поэтому, если вам нужна временная метка удаления, вы все равно можете получить ее из таблицы Envers REVINFO.

UPDATE

Другая идея заключается в том, чтобы использовать два столбца, а не один столбец. Вместо использования метки времени в качестве индикатора мягкого удаления, используйте битовый флаг

@Entity
@Audited
@SQLDelete("UPDATE person SET deleted = true WHERE id = ?")
@Where(clause = "deleted is NULL or deleted != true")
public class Person {
  @NotAudited
  private boolean deleted;
  private OffsetDateTime deleteTime;
  @PreRemove
  public void onPreRemove() {
    this.deleteTime = ...;
  }
}

Здесь я использую @PreRemove, чтобы установить значение времени удаления, которое затем должно быть передано в PostDeleteEvent в Envers и позволить Hibernate управлять битовым полем. А поскольку поле deleted никогда не указывается в состоянии события, я пометил его как @NotAudited, чтобы исключить его из схемы аудита.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...