java - 为后台线程创建 JPA session

标签 java spring hibernate jpa transactions

我们通过 JPA 和 Spring 使用 Hibernate 来管理 Web 应用程序中的对象持久性。我们使用 open-session-in-view 模式为响应 http 请求的线程创建 session 。我们还使用了一些不生成 View 的线程——它们只是不时地唤醒来完成它们的工作。这会产生问题,因为它们默认情况下没有打开 session ,所以它们会产生类似

的异常
org.hibernate.SessionException: Session is closed! 

 could not initialize proxy - no Session

我们发现,如果每个后台线程都在用 @Transactional 注释的方法中调用其逻辑,则不会出现此类异常,因为 @Transactional 确保线程具有 session 在事务内时。

它解决了一段时间的问题,但我认为这不是一个好的解决方案 - 使长时间运行的方法成为事务性的会导致问题,因为在提交事务之前其他线程无法看到数据库中所做的更改。

我创建了一个 java 伪代码示例来更好地说明我的问题:

public class FirstThread {

    ...

    @Transactional
    public void processQueue() {
        for(element : queue){
            if(elementCanBeProcessed(element)){
                elementDao.saveIntoDatabase(element);
                secondThread.addToQueue(element.getId());
            }
        }
    }

    private boolean elementCanBeProcessed(element){
        //code that gets a few objects from database and processes them
    }
}

如果我用 @Transactional 注释整个 processQueue 方法

elementDao.saveIntoDatabase(element);

在提交事务之前不会在 secondThread 中看到(因此直到处理完整个队列)。如果我不这样做,那么线程将不会在 elementCanBeProcessed 中进行 session ,并且它将无法访问数据库。我也不能注释 elementCanBeProcessed,因为它是此类中的私有(private)方法,我必须将它移到另一个类中,以便 Spring 代理可以工作。

是否可以将 session 绑定(bind)到线程而不使整个方法成为事务性的?我应该如何管理后台线程中的 session 和事务?

最佳答案

这是我在阅读 Amir Moghimi 的 answer 后编写的代码. 这似乎有点“hacky”,因为文档说典型的应用程序代码不应直接使用 EntityManagerHolder 和 TransactionSynchronizationManager。

@Service
public class DatabaseSessionManager {

    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;

    public void bindSession() {
        if (!TransactionSynchronizationManager.hasResource(entityManagerFactory)) {
            EntityManager entityManager = entityManagerFactory.createEntityManager();
            TransactionSynchronizationManager.bindResource(entityManagerFactory, new EntityManagerHolder(entityManager));
        }
    }

    public void unbindSession() {
        EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager
                .unbindResource(entityManagerFactory);
        EntityManagerFactoryUtils.closeEntityManager(emHolder.getEntityManager());
    }
}

它似乎在工作 - session 在 bindSession()unbindSession() 调用之间绑定(bind)到我的线程,我不必创建事务来实现这一目标。

关于java - 为后台线程创建 JPA session ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23732675/

相关文章:

java - 将 JPA sqlresultset 映射到自定义 POJO

java - 如何在 Velocity 2.0 中转义双引号?

java - 使用 AsyncTask 从 MainActivity 中的 my_Data 类读取数据

java keystore 文件限制

java - 我是否需要添加大量 "checks"以防止用户修改他们不应在 RESTful 应用程序中修改的数据?

java - 非法尝试将代理与两个打开的 session 相关联

java - 有没有办法在语言环境中获取与给定字符等效的所有字符

java - 当我添加 @Async 时,entityManager.flush() 将引发异常

java - Spring Data JPA findById() 抛出 ClassCastException

java - 使用 spring 和 hibernate 或 AtomicInteger 实现实时计数器,最好的方法是什么?