我有一个服务(控制器(,其中包含一个逻辑并创建用于底层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;
}
}
rp
注入RevisionProvider
实例:
@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 注入/启动事务并打开实体管理器,但仍然抛出该异常。
您能建议如何正确注入这里的实体管理器吗?
正在使用的框架版本:休眠:5.2.9.最终版版本:5.2.12.最终版
我假设你在这个上下文中使用 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.
}