java 在 Hibernate Envers 中获取先前版本的实体

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/793674/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:49:25  来源:igfitidea点击:

Get previous version of entity in Hibernate Envers

javahibernatejbosshibernate-envers

提问by razenha

I have an entity loaded by Hibernate (via EntityManager):

我有一个由 Hibernate 加载的实体(通过EntityManager):

User u = em.load(User.class, id)

This class is audited by Hibernate Envers. How can I load the previous version of a User entity?

此类由 Hibernate Envers 审核。如何加载以前版本的 User 实体?

采纳答案by Jason S

maybe this then (from AuditReaderdocs)

也许这就是(来自AuditReader文档)

AuditReader reader = AuditReaderFactory.get(entityManager);
User user_rev1 = reader.find(User.class, user.getId(), 1);

List<Number> revNumbers = reader.getRevisions(User.class, user_rev1);
User user_previous = reader.find(User.class, user_rev1.getId(),
  revNumbers.get(revNumbers.size()-1));

(I'm very new to this, not sure if I have all the syntax right, maybe the size()-1 should be size()-2?)

(我对此很陌生,不确定我的所有语法是否正确,也许 size()-1 应该是 size()-2?)

回答by Brad Mace

Here's another version that finds the previous revision relative to a "current" revision number, so it can be used even if the entity you're looking at isn't the latest revision. It also handles the case where there isn'ta prior revision. (emis assumed to be a previously-populated EntityManager)

这是另一个版本,它可以找到相对于“当前”修订号的先前修订,因此即使您正在查看的实体不是最新修订,也可以使用它。它还处理没有先前修订的情况。(em假定为先前填充的 EntityManager)

public static User getPreviousVersion(User user, int current_rev) {
    AuditReader reader = AuditReaderFactory.get(em);

    Number prior_revision = (Number) reader.createQuery()
    .forRevisionsOfEntity(User.class, false, true)
    .addProjection(AuditEntity.revisionNumber().max())
    .add(AuditEntity.id().eq(user.getId()))
    .add(AuditEntity.revisionNumber().lt(current_rev))
    .getSingleResult();

    if (prior_revision != null)
        return (User) reader.find(User.class, user.getId(), prior_revision);
    else
        return null
}

This can be generalized to:

这可以概括为:

public static T getPreviousVersion(T entity, int current_rev) {
    AuditReader reader = AuditReaderFactory.get(JPA.em());

    Number prior_revision = (Number) reader.createQuery()
    .forRevisionsOfEntity(entity.getClass(), false, true)
    .addProjection(AuditEntity.revisionNumber().max())
    .add(AuditEntity.id().eq(((Model) entity).id))
    .add(AuditEntity.revisionNumber().lt(current_rev))
    .getSingleResult();

    if (prior_revision != null)
        return (T) reader.find(entity.getClass(), ((Model) entity).id, prior_revision);
    else
        return null
}

The only tricky bit with this generalization is getting the entity's id. Because I'm using the Play! framework, I can exploit the fact that all entities are Models and use ((Model) entity).idto get the id, but you'll have to adjust this to suit your environment.

这种概括唯一棘手的一点是获取实体的 id。因为我正在使用 Play!框架,我可以利用所有实体都是模型并用于((Model) entity).id获取 id的事实,但您必须对其进行调整以适应您的环境。

回答by Jamie Bisotti

I think it would be this:

我想会是这样:

final AuditReader reader = AuditReaderFactory.get( entityManagerOrSession );

// This could probably be declared as Long instead of Object
final Object pk = userCurrent.getId();

final List<Number> userRevisions = reader.getRevisions( User.class, pk );

final int revisionCount = userRevision.size();

final Number previousRevision = userRevisions.get( revisionCount - 2 );

final User userPrevious = reader.find( User.class, pk, previousRevision );

回答by Rich Kroll

From the docs:

从文档:

AuditReader reader = AuditReaderFactory.get(entityManager);
User user_rev1 = reader.find(User.class, user.getId(), 1);

回答by Steven Spungin

Building off of the excellent approach of @brad-mace, I have made the following changes:

基于@brad-mace 的出色方法,我进行了以下更改:

  • You should pass in your EntityClass and Id instead of hardcoding and assuming the Model.
  • Don't hardcode your EntityManager.
  • There is no point setting selectDeleted, because a deleted record can never be returned as the previous revision.
  • Calling get single result with throw and exception if no results or more than 1 result is found, so either call resultlist or catch the exception (this solution calls getResultList with maxResults = 1)
  • Get the revision, type, and entity in one transaction (remove the projection, use orderBy and maxResults, and query for the Object[3] )
  • 您应该传入您的 EntityClass 和 Id,而不是硬编码和假设模型。
  • 不要对您的 EntityManager 进行硬编码。
  • 没有点设置selectDeleted,因为删除的记录永远不能作为以前的修订返回。
  • 如果未找到结果或找到超过 1 个结果,则调用 get single result with throw 和 exception,因此要么调用 resultlist,要么捕获异常(此解决方案调用 getResultList 时 maxResults = 1)
  • 在一个事务中获取修订版、类型和实体(移除投影,使用 orderBy 和 maxResults,并查询 Object[3] )

So here's another solution:

所以这是另一个解决方案:

public static <T> T getPreviousRevision(EntityManager entityManager, Class<T> entityClass, Object entityId, int currentRev) {
    AuditReader reader = AuditReaderFactory.get(entityManager);
    List<Object[]> priorRevisions = (List<Object[]>) reader.createQuery()
            .forRevisionsOfEntity(entityClass, false, false)
            .add(AuditEntity.id().eq(entityId))
            .add(AuditEntity.revisionNumber().lt(currentRev))
            .addOrder(AuditEntity.revisionNumber().desc())
            .setMaxResults(1)
            .getResultList();

    if (priorRevisions.size() == 0) {
        return null;
    }
    // The list contains a single Object[] with entity, revinfo, and type 
    return (T) priorRevision.get(0)[0];
}