Dropwizard @UnitOfWork with asynchronous database call

混江龙づ霸主 提交于 2021-02-07 08:56:06

问题


I imagine that this is a common problem, but after some searching I wasn't able to find anything relevant.

The problem I'm having is that I'm getting a No Hibernate Session bound to thread exception when annotating my resource method with @UnitOfWork and inside my resource method, making an asynchronous DAO call. The idea behind this design is to make the database call on a separate I/O thread so that it frees up the Jersey resource thread.

Unfortunately, as the exception says, this RxIoScheduler-2 thread doesn't have a hibernate session bound to it.

Any suggestions?


回答1:


Hibernate Session is not thread safe, so we need a strategy how to get the current session for the current thread. Such strategy is called CurrentSessionContext.

The current session is a session which we get by this call:

sessionFactory.getCurrentSession()

Hibernate can be configured with various current session strategies. @UnitOfWork uses this strategy:

hibernate.current_session_context_class = managed

For this strategy you should put a session to the context by an explicit call of the

ManagedSessionContext.bind(session)

So, as we know a Session is not thread safe, you should create a new session for a separate thread and put that session in the ManagedSessionContext. After that you can call your DAO by the same way as in the endpoint methods with @UnitOfWork.

Keep in mind that you should unbind the session before closing it with

ManagedSessionContext.unbind(factory)

You can use this utility class to create a session for a separate thread:

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

}

Throwables from guava.

It can be used by this way

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

In the dao.getCampaigns() method you can get the session

sessionFactory.getCurrentSession()

You can inject the factory everywhere using Guice.

Other option is to use UnitOfWorkAwareProxyFactory.



来源:https://stackoverflow.com/questions/47027268/dropwizard-unitofwork-with-asynchronous-database-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!