#jira #hibernate-envers
#jira #hibernate-envers
Вопрос:
Я пытаюсь показать историю изменений, подобную JIRA, в пользовательском интерфейсе. Я использую Spring Data JPA
и настроил журнал аудита с помощью Envers (версия 5.3.7). Я могу получить список всех ревизий, используя AuditQuery
, для конкретного объекта по его значению первичного ключа.
Есть ли простой способ вычислить «дельту» по ревизиям и определить свойства, которые были изменены? (Со старым и новым значением)
Я добавил @Audited(withModifiedFlag = true)
аннотацию к своему классу сущностей. Он добавляет еще один столбец в <entity>_aud
таблицу для каждого свойства, указывающий, было ли свойство изменено или нет. Я пытаюсь выяснить, как использовать эти дополнительные столбцы.
Комментарии:
1. С Envers 6.0 поставляется новый API, который позволяет вам использовать
withModifiedFlag=true
настройку атрибута и получать список ревизий для объекта, и он сообщит вам, какие поля были изменены в этой ревизии, поэтому вам не нужно использовать перехватчик или пользовательские таблицы; просто Envers.
Ответ №1:
Если вам нужно что-то вроде JIRA, вы должны создать его самостоятельно.
Я бы посоветовал вам использовать перехватчики Hibernate:
http://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#events
Как вы можете видеть в следующем примере, вы получаете текущее и предыдущее состояния, а затем можете создать дельту и сохранить ее в своей собственной таблице журнала изменений:
public static class LoggingInterceptor extends EmptyInterceptor {
@Override
public boolean onFlushDirty(
Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
LOGGER.debugv( "Entity {0}#{1} changed from {2} to {3}",
entity.getClass().getSimpleName(),
id,
Arrays.toString( previousState ),
Arrays.toString( currentState )
);
return super.onFlushDirty( entity, id, currentState,
previousState, propertyNames, types
);
}
}
Комментарии:
1. Спасибо Саймону, что указал мне правильное направление. Вместо использования EmptyInterceptor я использовал org.hibernate.event.spi.PreUpdateEventListener. У меня это сработало. Я публикую свой код в этой теме.
Ответ №2:
Вот мой код
import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventType;
import org.hibernate.event.spi.PreUpdateEvent;
import org.hibernate.event.spi.PreUpdateEventListener;
import org.hibernate.internal.SessionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class AuditListener implements PreUpdateEventListener {
@Autowired
private EntityManagerFactory entityManagerFactory;
@PostConstruct
private void init() {
SessionFactoryImpl sessionFactory = entityManagerFactory.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = sessionFactory.getServiceRegistry().getService(EventListenerRegistry.class);
// You can also add listener for a specific entity-type instead of event-group
// In my case I needed global event listener
registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(this);
}
@Override
public boolean onPreUpdate(PreUpdateEvent event) {
String[] propertyNames = event.getPersister().getPropertyNames();
Object[] oldValues = event.getOldState();
Object[] newValues = event.getState();
for (int index = 0; index < propertyNames.length; index ) {
String propertyName = propertyNames[index];
Object oldValue = oldValues[index];
Object newValue = newValues[index];
// This is just sample code
boolean changed = oldValue != newValue;
if (changed) {
System.out.println("Audit log -> Property: " propertyName ", Old value: " oldValue ", New value: " newValue);
// Actual code that persists audit log
...
...
}
}
return false;
}
}
Комментарии:
1. Как бы вы добавили прослушиватель для определенного типа объекта?