问题
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