如何判断当前会话是否脏?

Roe*_*rel 8 java hibernate jpa spring-mvc

当且仅当数据库发生更改时,我想发布一个事件。我在 @Transaction 下运行的是 Spring 上下文,我想出了这个检查:

    Session session = entityManager.unwrap(Session.class);
    session.isDirty();
Run Code Online (Sandbox Code Playgroud)

对于新的(瞬态)对象,这似乎失败了:

@Transactional
public Entity save(Entity newEntity) {
    Entity entity = entityRepository.save(newEntity);
    Session session = entityManager.unwrap(Session.class);
    session.isDirty(); // <-- returns `false` ):
    return entity;
}
Run Code Online (Sandbox Code Playgroud)

根据这里的答案/sf/answers/368803221/我希望它能够工作并返回 true。

我错过了什么?

更新
考虑到@fladdimir 的回答,虽然这个函数是在事务上下文中调用的,但我确实@Transactional在函数上添加了(来自 org.springframework.transaction.annotation)。但我仍然遇到相同的行为。isDirty 返回 false。

此外,正如预期的那样,当程序在session.isDirty().

UPDATE_2
我还尝试在调用 repo save 之前更改会话刷新模式,也没有任何影响:

    session.setFlushMode(FlushModeType.COMMIT);
    session.setHibernateFlushMode(FlushMode.MANUAL);
Run Code Online (Sandbox Code Playgroud)

Roe*_*rel 5

首先,Session.isDirty()和我理解的意思不一样。它告诉当前会话是否在内存中保存尚未发送到数据库的查询。虽然我认为它可以告诉交易是否有变化的查询。保存新实体时,即使在事务中,也必须将插入查询发送到数据库才能获取新实体 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

为了使用它,我必须:

  • 扩展SessionFactoryImpl.SessionBuilderImpl以覆盖该openSession函数并返回我的CustomSession
  • 扩展SessionFactoryImpl覆盖withOptions函数以返回扩展SessionFactoryImpl.SessionBuilderImpl
  • 扩展AbstractDelegatingSessionFactoryBuilderImplementor覆盖build函数以返回扩展SessionFactoryImpl
  • 实现SessionFactoryBuilderFactory返回getSessionFactoryBuilder扩展AbstractDelegatingSessionFactoryBuilderImplementor
  • org.hibernate.boot.spi.SessionFactoryBuilderFactory在 META-INF/services 下添加文件,其中包含我的SessionFactoryBuilderFactory实现完整类名的值(以便 spring 知道它)。

更新
捕获“合并”调用时存在一个错误(如巨大的7评论),因此我最终在任何刷新之前捕获 isDirty 状态,并在检查时再次检查它isChanged()