【发布时间】:2018-11-15 22:24:09
【问题描述】:
注意:这不是一个重复的问题。阅读整篇文章;)
为了防止 XY 问题,这里是完整的故事:
我需要编写一个 Envers 修订监听器来计算和持久化 Spring 应用程序中实体最近更改的差异:
public class MyRevisionListener implements EntityTrackingRevisionListener {
@Override
public void newRevision(Object revision) {
...
}
@Override
public void entityChanged(Class entityClass,
String entityName,
Serializable entityId,
RevisionType revisionType,
Object revisionEntity) {
EntityManager em = EntityManagerBeanLookup.getInstance().get();
int id = ((DefaultRevisionEntity) revisionEntity).getId();
List<?> revisions = AuditReaderFactory.get(em)
.createQuery()
.forRevisionsOfEntity(entityClass, false, true)
.add(AuditEntity.id().eq(entityId))
.add(AuditEntity.revisionNumber().le(id + 1))
.addOrder(AuditEntity.revisionNumber().desc())
.setMaxResults(2)
.getResultList();
checkArgument(revisions.size() > 0, "Need at least one revision: %s", revisions);
// continue with calculating and persisting the diff;
}
}
要获得EntityManager bean,我有实用程序/bean EntityManagerBeanLookup:
@Component
public class EntityManagerBeanLookup implements Supplier<EntityManager> {
@Getter
private static EntityManagerBeanLookup instance;
@Getter
private EntityManager entityManager;
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
@Autowired
public void setInstance(EntityManagerBeanLookup instance) {
EntityManagerBeanLookup.instance = instance;
}
@Override
public EntityManager get() {
return ofNullable(getInstance())
.map(EntityManagerBeanLookup::getEntityManager)
.orElseThrow(() -> new IllegalStateException("Not initialized yet"));
}
}
一切正常。问题是当我们使用 Spring Test 框架进行集成测试时。根据 Spring 创建测试上下文和缓存 bean 的方式,bean 查找类可能会从另一个上下文返回实体管理器,而不是当前上下文。如果您在其他测试中运行它,当通过测试失败时,在我们的案例中就会发生这种情况(更多信息here)。
现在的问题:
1.有没有办法将MyRevisionListener定义为Spring bean,这样我就可以干净地注入持久化上下文了?
2. 如果以上为否,那么如何在静态上下文/方法中正确获取持久性上下文?
谢谢。
【问题讨论】:
-
想知道为什么投反对票?
-
如果只是在测试失败时才出现问题,那可能是因为之前的测试没有正确清除或拆除某些东西,从而污染了后续测试的问题吗?也许更详细一点可以解决这个问题。
-
我的理解是:Spring为测试A创建上下文A(在这个上下文中,静态字段
instance指向上下文A中的Bean A),测试通过,然后Spring为测试A创建上下文B测试 B,因为它具有不同的配置并且需要不同的上下文(在此上下文中,静态字段instance指向上下文 B 中的 Bean B)。现在 Spring 为测试 C 选择上下文 A,因为它具有相同的配置并且可以使用上下文 A 进行测试(现在静态字段instance指向上下文 B 中的 Bean B)。你觉得这有意义吗? -
Roman 的回答绝对正确。过去我已经谈到如何在
RevisionListener中获取spring bean,最有效的方法是将所需的任何内容注入线程局部变量并在侦听器中访问它。一个很好的例子是 spring security 的SecurityContextHolder。在 Hibernate 5.3 中,我们将RevisionListener暴露为托管 bean,这意味着它现在可以是@Component或类似 spring-bean 的任何东西;但是 Spring 尚未实现对此的支持,请参阅 jira.spring.io/browse/SPR-16305 -
谢谢@Naros,太棒了:)
标签: spring hibernate spring-data-jpa hibernate-envers