【问题标题】:Hibernate Envers : How to check if a field has changed between two revisions?Hibernate Envers:如何检查一个字段是否在两个修订版之间发生了变化?
【发布时间】:2016-07-16 14:20:06
【问题描述】:

我正在使用 Hibernate envers 来审核我的应用程序中的实体。我为每个实体都有单独的 _audit 表,然后在这些表中我有 _mod 布尔列来指示该字段是否已更改。

但是,我不知道如何在查询中使用该列,甚至不知道如何在代码中获取这些数据?

例如以下代码给出了被审计人员的名单。如何检查哪些数据发生了变化?

List person = getAuditReader().createQuery()
    .forEntitiesAtRevision(Person.class, 12)
    .getResultList();

【问题讨论】:

  • 正如我在回答中所说:您可以在拦截器的 onFlushDirty 方法中查明实体是否已更改,并将此信息保存到您的审计表中。尝试检查 previousState 和 currentState 参数。

标签: java spring hibernate-envers


【解决方案1】:

您可以遍历给定实体的所有字段,并查看该字段是否已更改。

示例代码 sn-p 循环遍历修订号 12 的 Person 实体的所有字段并查找已更新字段的值。

  public void processFields() throws Exception {
    for (Field field : Person.class.getDeclaredFields()) {
      final Person changedEntity = fetchEntityForRevisionWhenPropertyChanged(Person.class, 12, field.getName());
      if (changedEntity != null) {
        // Updated field value for the field. This will list all fields which are changed in given revision.
        final Object fieldValue = getField(changedEntity, field.getName());
      }
    }
  }

  private  <T> Object getField(final T object, final String fieldName) throws Exception {
    return new PropertyDescriptor(fieldName, object.getClass()).getReadMethod().invoke(object);
  }

  private  <T> T fetchEntityForRevisionWhenPropertyChanged(final Class<T> entityClass, final int revisionNumber, final String propertyName) {
    final List<T> resultList = getAuditReader()
        .createQuery()
        .forEntitiesModifiedAtRevision(entityClass, revisionNumber)
        .add(AuditEntity.property(propertyName).hasChanged())
        .addOrder(AuditEntity.id().asc())
        .getResultList();

    if (CollectionUtils.isNotEmpty(resultList)) {
      return resultList.get(0);
    } else {
      return null;
    }
  }

在任何情况下,如果您想查找您的实体的以前的修订版本进行比较,您可以使用以下方法:

  public T getPreviousVersion(final Class<T> entityClass, final Long entityId, final Long currentRevisionNumber) {
    final AuditReader reader = AuditReaderFactory.get(entityManager);

    final Number previousRevision = (Number) reader.createQuery()
        .forRevisionsOfEntity(entityClass, false, true)
        .addProjection(AuditEntity.revisionNumber().max())
        .add(AuditEntity.id().eq(entityId))
        .add(AuditEntity.revisionNumber().lt(currentRevisionNumber))
        .getSingleResult();

    return Optional.ofNullable(previousRevision)
        .map(previousRev -> reader.find(entityClass, entityId, previousRev))
        .orElse(null);
  }

【讨论】:

    【解决方案2】:

    您可以尝试使用 Hibernate 拦截器。 Here is关于拦截器的好文章。 使用拦截器,您可以创建一个回调,该回调将在实体更新/创建等时执行。它会像这样:

    public class EntityInterceptor extends EmptyInterceptor {
        @Override
        public boolean onFlushDirty(Object entity,
                                    Serializable id,
                                    Object[] currentState,
                                    Object[] previousState,
                                    String[] propertyNames,
                                    Type[] types) {
            if ( entity instanceof YourEntity ) {
                //do update stuff
                return true;
            }
            return false;
        }
    }
    

    您可以将 currentStatepreviousState 进行比较,以收集有关实体更改的信息并将其保存到其他表中。

    【讨论】:

    • 我已经在使用 envers 来保存实体更改。我只需要查询表以获取正确的数据。
    • 我不明白这个问题......你能更具体一点吗?您有每个实体的审计表,并且您希望在实体修改时填充它们。正确的?那么问题是什么?
    • 我可以正确填充它们。在审计表中插入数据没有问题。问题出在从审计表中获取详细信息时。
    • 什么样的问题?
    猜你喜欢
    • 2014-03-02
    • 2013-07-08
    • 1970-01-01
    • 2010-10-23
    • 1970-01-01
    • 2010-11-07
    • 2013-12-29
    • 2011-02-28
    • 2022-01-20
    相关资源
    最近更新 更多