首页 文章

Hibernate Envers“InvalidDataAccessApiUsageException:关联的实体管理器已关闭”

提问于
浏览
0

我有一个服务(控制器),其中包含一个逻辑,并为底层DAO对象创建事务 .

在其中一个方法中,我从数据库中读取实体,然后尝试使用Hibernate Envers找到它的历史版本 .

@Named
@Transactional
public class DocCtrl {
  ...

public synchronized List<DocCreateRespDTO> getUnapprovedVersions() {
        List<Task> unapprovedTasks = taskDAO.getForApproval();

        List<DocCreateRespDTO> unapprovedDocDTOS = new ArrayList<>(unapprovedTasks.size());

        for (Task u : unapprovedTasks) {
            ...

            rp.setManipulationCtx(dto.document, active.getId());

            unapprovedDocDTOS.add(dto);
        }

        return unapprovedDocDTOS;
    }

}

rpRevisionProvider 的注入实例:

@Repository
public class RevisionProvider {

    @PersistenceContext(name = Vedantas.PU_NAME, type = PersistenceContextType.TRANSACTION)
    private EntityManager entityManager;

    private AuditReader auditReader;

    @PostConstruct
    void init() {
        auditReader = AuditReaderFactory.get(entityManager);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public OperationCtx getCreationRevisionFor(Class aClass, long id) {
        final Object[] array = (Object[]) auditReader.createQuery()
                .forRevisionsOfEntity(aClass, false, false)
                .add(AuditEntity.revisionType().eq(RevisionType.ADD))
                .add(AuditEntity.id().eq(id))
                .setMaxResults(1)
                .getSingleResult();

        return ((OperationCtx) array[1]);
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public OperationCtx getLastRevisionFor(Class aClass, long id) {
        final Object[] array = (Object[]) auditReader.createQuery()
                .forRevisionsOfEntity(aClass, false, false)
                .add(AuditEntity.id().eq(id))
                .add(AuditEntity.revisionNumber().maximize()
                        .computeAggregationInInstanceContext()
                )
                .addOrder(AuditEntity.revisionNumber().desc())
                .setMaxResults(1)
                .getSingleResult();

        OperationCtx lastchg = (OperationCtx) array[1];
        OperationCtx creation = getCreationRevisionFor(aClass, id);

        if (creation.getId() == lastchg.getId())
            return new OperationCtx();

        return lastchg;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public List getHistoryFor(Class aClass, String pk, String val) {
        List res = auditReader.createQuery()
                .forRevisionsOfEntity(aClass, false, false)
                .add(AuditEntity.property(pk).eq(val))
                .getResultList();

        return res;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void setManipulationCtx(FindDTO dto, long id) {
        OperationCtx cctx = getCreationRevisionFor(DocVersion.class, id);
        OperationCtx lctx = getLastRevisionFor(Metadata.class, dto.metadata.getId());

        dto.createCtx = ModelCopyHelper.copyOperCtx(cctx);
        dto.lastChangeCtx = ModelCopyHelper.copyOperCtx(lctx);
    }
}

现在,当执行程序命中 auditReader.createQuery() 时,会发生 WARNING: org.springframework.dao.InvalidDataAccessApiUsageException: The associated entity manager is closed!; 被抛出的情况 .

正如您所看到的,我尝试为每个方法注释一个事务,以使spring注入/启动事务并打开实体管理器,但仍然抛出该异常 .

你能建议如何正确地注入实体经理吗?

正在使用的框架版本:Hibernate:5.2.9.Final Envers:5.2.12.Final

1 回答

  • 0

    我将假设您在此上下文中使用Spring及其默认bean定义;因此,您无法创建有状态bean并将其作为无状态bean中的实例变量进行缓存,并以这种方式跨州边界重用它 . 这就是你得到错误的原因 .

    您需要做的是延迟构建 AuditReader 直到您需要它为止 . 这可以保证底层 EntityManager 对该执行线程是开放的 .

    简而言之,您需要这样做:

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public OperationCtx getCreationRevisionFor(Class aClass, long id) {
      final AuditReader auditReader = AuditReaderFactory.get( entityManager );
      // do whatever you need with the reader here.
    }
    

相关问题