java - 具有异步数据库调用的 Dropwizard @UnitOfWork

标签 java multithreading hibernate rx-java dropwizard

我认为这是一个常见问题,但经过一番搜索后我找不到任何相关内容。

我遇到的问题是,当使用 @UnitOfWork 注释我的资源方法并在我的资源方法内部时,我得到了一个 No Hibernate Session bound to thread 异常, 进行异步 DAO 调用。此设计背后的想法是在单独的 I/O 线程上进行数据库调用,以便释放 Jersey 资源线程。

不幸的是,如异常所述,此 RxIoScheduler-2 线程没有绑定(bind) hibernate session 。

有什么建议吗?

最佳答案

Hibernate Session 不是线程安全的,所以我们需要一个策略来获取当前线程的当前 session 。这种策略称为 CurrentSessionContext

当前 session 是我们通过这个调用得到的 session :

sessionFactory.getCurrentSession()

Hibernate 可以配置各种当前 session 策略。 @UnitOfWork 使用此策略:

hibernate.current_session_context_class = managed

对于此策略,您应该通过显式调用

将 session 置于上下文中
ManagedSessionContext.bind(session)

因此,正如我们所知,Session 不是线程安全的,您应该为单独的线程创建一个新 session 并将该 session 放入 ManagedSessionContext 中。之后,您可以通过与使用 @UnitOfWork 的端点方法相同的方式调用您的 DAO。

请记住,您应该在关闭 session 之前取消绑定(bind)

ManagedSessionContext.unbind(factory)

您可以使用此实用程序类为单独的线程创建 session :

public final class HibernateSessionUtils {

    private HibernateSessionUtils() {

    }

    public static void request(SessionFactory factory, Runnable request) {
        request(factory, () -> {
            request.run();
            return null;
        });
    }

    public static <T> T request(SessionFactory factory, Supplier<T> request) {
        Transaction txn = null;
        Session session = factory.openSession();

        try {
            ManagedSessionContext.bind(session);
            txn = session.beginTransaction();
            T result = request.get();
            commit(txn);
            return result;
        } catch (Throwable th) {
            rollback(txn);
            throw Throwables.propagate(th);
        } finally {
            session.close();
            ManagedSessionContext.unbind(factory);
        }
    }

    private static void rollback(Transaction txn) {
        if (txn != null && txn.isActive()) {
            txn.rollback();
        }
    }

    private static void commit(Transaction txn) {
        if (txn != null && txn.isActive()) {
            txn.commit();
        }
    }

}
来自 guava

Throwables

可以这样使用

List<Campaign> getCampaigns(SessionFactory factory, CampaignDao dao) {
    return HibernateSessionUtils.request(
            factory,
            dao::getCampaigns
    );
}

dao.getCampaigns()方法中可以获取session

sessionFactory.getCurrentSession()

您可以使用 Guice 在任何地方注入(inject)工厂。

其他选项是使用 UnitOfWorkAwareProxyFactory

关于java - 具有异步数据库调用的 Dropwizard @UnitOfWork,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47027268/

相关文章:

java - 当我的项目中没有任何实体时,如何将 native 查询映射到 POJO?

java - Spring Data JPA - 实体名称无效标识符

java - Web 服务接受对象数组

java - Gson 有类似 WRITE_NULL_MAP_VALUES (Jackson) 的东西吗?

c# - Android C2DM 服务器端

java - 更新 SQLite 数据库中的行会创建重复行

c - 如何在 C 中同时打印两个函数?

java - Java 程序的输出

hibernate - 将数据从 View 传递到Grails中的 Controller 时传递参数而不调用 hibernate (GORM)

android - 即使手机处于 sleep 状态,也会在后台运行上传过程