Я хотел бы реализовать метод репозитория void touch(MyEntity myEntity)
, который обеспечивает вызов SQL обновления столбцов сущностей до их текущих значений. (Причина заключается в триггере on update
, который необходимо вызывать в какой-то момент выполнения.) Идеальный вариант использования:
void serviceMethod(Long myEntityId) {
MyEntity myEntity = myEntityRepository.findOne(myEntityId);
...
myEntityRepository.touch(myEntity);
...
}
Уже есть похожие вопросы по SO, которые у меня не работают: Принудительное обновление в Hibernate (моя сущность отсоединена), Реализация «прикосновения» к сущности JPA? (выполнение каких-то безобидных изменений работает, но не является общим и плохо влияет на читаемость кода), Обновление Hibernate Idempotent (аналогичный пример).
Мне известен метод перехвата сеанса findDirty
, а также CustomEntityDirtinessStrategy
, оба описаны в этой статьи Влада Михалчеа . Тем не менее, он, похоже, использует findDirty
Я должен был бы переопределить перехватчик сеанса, что невозможно изнутри метода репозитория, так как перехватчик является конечным полем, назначенным сеансу при создании сеанса. И CustomEntityDirtinessStrategy
происходит от SessionFactory
, который является глобальным. Мне скорее нужно какое-то одноразовое решение, чтобы временно считать одну конкретную сущность одного конкретного класса грязной.
На сегодняшний день наилучшим рабочим решением является установка недопустимого (массива нулей) снимка сущности в контекст постоянства, поэтомучто последующая логика в flush()
оценивает сущность как отличающуюся от снимка и обеспечивает обновление. Это работает:
@Override
@Transactional
public void touch(final T entity) {
SessionImpl session = (SessionImpl)em.getDelegate();
session.update(entity);
StatefulPersistenceContext pctx = (StatefulPersistenceContext) session.getPersistenceContext();
Serializable id = session.getIdentifier(entity);
EntityPersister persister = session.getEntityPersister(null, entity);
EntityKey entityKey = session.generateEntityKey(id, persister);
int length = persister.getPropertyNames().length;
Field entitySnapshotsByKeyField = FieldUtils.getField(pctx.getClass(), "entitySnapshotsByKey", true);
Map<EntityKey,Object> entitySnapshotsByKey = (Map<EntityKey,Object>)ReflectionUtils.getField(entitySnapshotsByKeyField, pctx);
entitySnapshotsByKey.put(entityKey, new Object[length]);
session.flush();
em.refresh(entity);
}
Совет в Принудительное обновление в Hibernate у меня не сработало, потому что session.evict(entity)
очищает entitySnapshotsByKey
запись вообще, что вызывает последующие org.hibernate.event.internal.DefaultFlushEntityEventListener#getDatabaseSnapshot
загрузкисвежая сущность из БД. Вопросу 9 лет, и я не уверен, применим ли он к текущей версии Hibernate (у меня 5.2.17).
Хотя я не удовлетворен таким хакерским решением. Есть ли какой-то простой способ или что-то, что я мог бы сделать проще?