我想发布一个事件时,并且只有当有更改的DB。我在@Transaction is Spring上下文下运行,并给出了下面的检查:
Session session = entityManager.unwrap(Session.class);
session.isDirty();对于新的(瞬态)对象来说,这似乎失败了:
@Transactional
public Entity save(Entity newEntity) {
Entity entity = entityRepository.save(newEntity);
Session session = entityManager.unwrap(Session.class);
session.isDirty(); // <-- returns `false` ):
return entity;
}基于这里的答案,https://stackoverflow.com/a/5268617/672689,我希望它能工作,并返回true。
我遗漏了什么?
更新
考虑到@fladdimir答案,尽管这个函数是在事务上下文中调用的,但我确实在函数上添加了@Transactional (来自org.springframework.transaction.annotation)。但我还是会遇到同样的行为。isDirty返回false。
此外,正如预期的那样,当程序停留在session.isDirty()行的断点时,新实体不会显示在DB上。
UPDATE_2
我还尝试在调用回购保存之前更改会话刷新模式,也没有任何效果:
session.setFlushMode(FlushModeType.COMMIT);
session.setHibernateFlushMode(FlushMode.MANUAL);发布于 2021-05-26 07:38:49
首先,Session.isDirty()的含义与我所理解的不同。它告诉当前会话是否存在内存查询,这些查询仍未发送到DB。虽然我认为它可以告诉事务是否有变化的查询。在保存新实体时,即使是在事务中,插入查询也必须发送到DB,以便获得新的实体id,因此isDirty()在之后始终为false。
因此,我最终创建了一个类来扩展SessionImpl并保存会话的change状态,并在持久化和合并调用( hibernate正在使用的函数)上更新它。
这就是我所写的课程:
import org.hibernate.HibernateException;
import org.hibernate.internal.SessionCreationOptions;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.SessionImpl;
public class CustomSession extends SessionImpl {
private boolean changed;
public CustomSession(SessionFactoryImpl factory, SessionCreationOptions options) {
super(factory, options);
changed = false;
}
@Override
public void persist(Object object) throws HibernateException {
super.persist(object);
changed = true;
}
@Override
public void flush() throws HibernateException {
changed = changed || isDirty();
super.flush();
}
public boolean isChanged() {
return changed || isDirty();
}
}为了使用它,我不得不:
SessionFactoryImpl.SessionBuilderImpl以覆盖openSession函数并返回我的CustomSessionSessionFactoryImpl以覆盖withOptions函数以返回扩展的SessionFactoryImpl.SessionBuilderImplAbstractDelegatingSessionFactoryBuilderImplementor以覆盖build函数以返回扩展的SessionFactoryImplSessionFactoryBuilderFactory以实现getSessionFactoryBuilder以返回扩展的AbstractDelegatingSessionFactoryBuilderImplementororg.hibernate.boot.spi.SessionFactoryBuilderFactory文件,其值为我的SessionFactoryBuilderFactory实现完整类名(为了让春季知道它)。更新
在捕获“合并”调用(作为tremendous7注释)时出现了一个错误,因此我在任何刷新之前都会捕获isDirty状态,并且在检查isChanged()时也会再次检查它。
发布于 2021-05-24 23:43:36
下面是一种不同的方法,您可以利用它来跟踪污秽。
虽然在架构上与示例代码不同,但它可能更接近您的实际目标(,我想在DB发生更改时才发布事件)。
也许您可以使用Interceptor侦听器让实体管理器执行繁重的任务,然后告诉您什么是脏的。然后你只需要对它做出反应,而不是去刺激它,从一开始就找出什么是肮脏的。
看一看本文:https://www.baeldung.com/hibernate-entity-lifecycle
它有很多测试用例,这些测试用例基本上检查保存在各种上下文中的对象的脏度,然后它依赖于一段名为DirtyDataInspector的代码,该代码有效地侦听在刷新时标记为脏的任何项,然后只记住它们(即将它们保存在列表中),这样单元测试用例就可以断言应该是脏的东西实际上已经被刷新为脏了。
脏数据检查器代码在他们的github上。这是便于访问的直接链接。
下面是将拦截器应用于工厂的代码,这样它才能有效。您可能需要相应地在注入框架中使用把这个写下来。
它所基于的拦截器的代码有大量的生命周期方法,您可能可以利用这些方法来获得完美的行为,“如果实际发生了肮脏的保存,请执行此操作”。
您可以看到它的完整文档这里。
发布于 2021-05-20 18:02:01
我们不知道您的完整设置,但正如@Christian Beikov在评论中所建议的,在您调用isDirty()之前,插入是否已经被刷新了?
这将发生在没有运行事务的情况下调用repository.save(newEntity)时,因为SimpleJpaRepository的save方法是用@Transactional注释自己的。
@Transactional
@Override
public <S extends T> S save(S entity) {
...
}如果没有一个已处于活动状态的事务,这将将调用包装在一个新事务中,并在该方法返回之前将插入刷新到事务结束时的DB。
您可以选择用save和isDirty对调用@Transactional的方法进行注释,以便在调用方法时创建事务,并传播到存储库调用。这样,当save返回时事务就不会提交,会话仍然是脏的。
(为了完整起见,编辑:如果使用identity ID生成策略,在提交运行中的事务之前,在存储库的save调用期间刷新新创建实体的插入以生成ID )
https://stackoverflow.com/questions/67619442
复制相似问题