Dropwizard @UnitOfWork 与异步数据库调用

315*_*315 4 java multithreading hibernate dropwizard rx-java

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

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

不幸的是,正如异常所说,该RxIoScheduler-2线程没有绑定到它的休眠会话。

有什么建议么?

v.l*_*nev 5

HibernateSession不是线程安全的,所以我们需要一个策略如何获取当前线程的当前会话。这种策略称为CurrentSessionContext

当前会话是我们通过以下调用获得的会话:

sessionFactory.getCurrentSession()
Run Code Online (Sandbox Code Playgroud)

Hibernate 可以配置各种当前会话策略。@UnitOfWork使用这个策略:

hibernate.current_session_context_class = managed
Run Code Online (Sandbox Code Playgroud)

对于此策略,您应该通过显式调用将会话置于上下文中

ManagedSessionContext.bind(session)
Run Code Online (Sandbox Code Playgroud)

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

请记住,您应该在关闭会话之前取消绑定会话

ManagedSessionContext.unbind(factory)
Run Code Online (Sandbox Code Playgroud)

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

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();
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Throwablesguava

可以通过这样的方式使用

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

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

sessionFactory.getCurrentSession()
Run Code Online (Sandbox Code Playgroud)

您可以使用 到处注入工厂Guice

其他选项是使用UnitOfWorkAwareProxyFactory.