Dropwizard #UnitOfWork with asynchronous database call - java

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?

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.

Related

What happen on Transaction.rollback in nested session/transactions?

Consider this two classes: EmployeeDetailDAOImpl and EmployeeDAOImpl. Assume if I want to create a new employee, I should also create a new record for EmployeeDetail.
Given the below implementation, I wonder if the outer transaction(EmployeeDAOImpl's tx) is rolled back due to any exceptions happened after the detailDAO.create(employeeId) call, will the transaction of new EmployeeDetail be rolled back as well?
public class SessionHandler {
public static getSession() {
return Configuration.buildSessionFactory().openSession(); //ignore the isConnected or other exception handling for now
}
}
public class EmployeeDetailDAOImpl {
public void create(Serializable employeeId) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
EmployeeDetail detail = new EmployeeDetail(employeeId);
session.save(detail );
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
public class EmployeeDAOImpl {
public void add(String name) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
Employee employee = new Employee(name);
Serializable employeeId= session.save(employee);
EmployeeDetailDAOImpl detailDAO = new EmployeeDetailDAOImpl();
detailDAO.create(employeeId);
//more things here, that may through exceptions.
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
Actually, none of the given answers is 100% correct.
It depends on the calling party/service.
If you are calling the methods from an EJB, you will have 1 transaction covering both method calls. That way, the transaction will roll back both operations in case of an exception. Reason behind this is that every method in EJB is transaction Required, unless specified otherwise in the annotation or ejb deployment descriptor.
If you are using spring or any other DI framework, then it depends on your configuration. In a normal setup, your calling transaction will be suspended, since the JPA EJB will create its own transaction. You can however use the JTATransactionManager (As specified here) to make sure that both your EJB and your Spring bean share the same transaction.
If you call the JPA methods from a POJO, then you will have to take care of the JTA transaction handling yourself.
Yes, it will rollback the entity Employee as well. It doesn't even depend on whether the other entities are related.
It depends on the scope of the transaction, which here includes both Employee and EmployeeDetails
You are creating two different transaction for each method.Hence rollback can not happen.
To rollback the transaction you require the propogation in Transaction.
You need to write the code like below::
#Transactional(propagation=Propagation.REQUIRED)
public void testRequired(User user) {
testDAO.insertUser(user);
try{
innerBean.testRequired();
} catch(RuntimeException e){
// handle exception
}
}
Below is link for more information of Propogation.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/transaction/annotation/Propagation.html
http://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial

Use jOOQ with Play Framework

I'm trying to create a base project with Play Framework 2.5 and jOOQ 3.9 using PostgreSQL 9.5 as database. This project is structured in three main layers: controllers, services and daos (and I only have one instance of every service and dao due to Guice dependency injection).
To work with jOOQ transactions over controller's methods I have developed the following Java Action:
public class DatabaseTransactionAction extends Action.Simple {
#Inject
private Database database;
private Connection connection;
#Override
public CompletionStage<Result> call (Http.Context ctx) {
connection = database.getDataSource().getConnection();
// Make JOOQ work with the connection acquired
DSLContext dslContext = DSL.using (connection,
SQLDialect.POSTGRES, new Settings());
// Wrap everything inside the same transaction
return dslContext.transactionResultAsync (configuration -> {
/**
* Save the transaction configuration in the action context arguments so that
* the action can pass down the same transaction into the services it depends on
*/
ctx.args.put (Constants.DATABASE_TRANSACTION_KEY, configuration);
// Make the call to the action itself
return delegate.call (ctx);
// Commit and release the connection when the action terminates
}).thenComposeAsync (resultCompletionStage -> {
return resultCompletionStage.thenApply (result -> {
try {
this.connection.commit();
JDBCUtils.safeClose (this.connection);
} catch (Exception e) {
throw new DatabaseException (e);
}
return result;
});
// In case something wrong happens, rollback the transaction
}).exceptionally (throwable -> {
try {
this.connection.rollback();
JDBCUtils.safeClose (this.connection);
} catch (Exception e) {
throw new DatabaseException (e);
}
throw new DatabaseException (throwable);
});
}
}
The problem of this approach is that I need to create a new class (very similar to jOOQ DAOImpl class) to use the configuration stored in the Http context:
public abstract class DAOImpl<R extends UpdatableRecord<R>, P, T> implements DAO<R, P, T> {
...
#Override
public /* non-final */ Configuration configuration() {
if (Http.Context.current.get() != null)
return (Configuration) Http.Context.current().args.get (Constants.DATABASE_TRANSACTION_KEY);
return this.configuration;
}
...
#Override
public /* non-final */ List<P> findAll() {
return using(configuration())
.selectFrom(table)
.fetch()
.map(mapper());
}
...
}
Other problem is that I need to set the configuration manually if I try to access to the database using a Dao when the Http context does not exist yet (in an ActionCreator for example).
I have read several links about it:
jOOQ initializing DAO Best Approach
jOOQ examples
But I'm not sure what is the best way to do the following:
Work with only one configuration due to I only has one database.
I would like to know if there is a better approach than I have developed, that is, without "overwrite" the DAOImpl class (taking into account that I would like to have only one instance of every Dao)

ConcurrentModificationException upon committing transaction with Hibernate

In our application we have upgraded from Hibernate 3.5.6-final to 4.2.21.Final and now we are getting a ConcurrentModificationException when a database transaction is committed:
java.util.ConcurrentModificationException: null
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:386)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:304)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:349)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1195)
at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:404)
at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
Is this a known issue with Hibernate 4.2?
The exception turned out to be caused by a problem with a Hibernate custom constraint validator we use. The validator's isValid was running a Hibernate criteria query. The query triggered a Hibernate session flush which resulted in the ConcurrentModificationException. We fixed the problem by temporarily disabling auto flush in the isValid method:
#Override
public boolean isValid(Object object, final ConstraintValidatorContext c) {
try {
sessionFactory.getCurrentSession().setFlushMode(FlushMode.MANUAL);
...
} finally {
sessionFactory.getCurrentSession().setFlushMode(FlushMode.AUTO);
}
}
The problem may also manifest itself as a StackOverflowError.
I had this with hibernate 5.0.11 and verified it happened also with 5.2.5. My solution was to annotate the custom validator to open new transaction.
#Transactional(propagation=Propagation.REQUIRES_NEW)
I guess hibernate has still some way to go before custom constraint validators are made easy to setup and use since this took me way more time than it should.
Edit: Issue related. I think using the same transaction violates jpa2.1 specs https://hibernate.atlassian.net/browse/HHH-7537
In the class which implements a ConstraintValidator, we need to have an instance of EntityManager but we are not in a Spring context in order to instanciate automatically an EntityManager object with annotation #Autowired. So, in the configuration package, we can write a factory wich allows to have an instance of Application in order to instanciate beans when we are not in a Spring context.
#Configuration
public class ApplicationContextConf {
#Bean
public static ApplicationContextProvider contextProvider() {
return new ApplicationContextProvider();
}
}
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(final ApplicationContext ctx) {
context = ctx;
}
}
In the class which implements a ConstraintValidator, we instanciate an EntityManager thanks to the factory created previously in the method initialize.
Before to call to a method which calls the repository, we change the flush mode of the Hibernate current session to FlushMode.MANUAL in order to avoid having the automatic flush after the call to the repository while keeping the default flush mode. In the block finally, we restore the default value of the flush mode previously retained.
private EntityManager entityManager;
#Override
public void initialize(final Object object ) {
// ...
try {
this.entityManager = ApplicationContextConf
.contextProvider()
.getApplicationContext()
.getBean(EntityManager.class);
}
catch (final BeansException ex) {
// ...
}
}
#Override
public boolean isValid(final Object object, final ConstraintValidatorContext context) {
Session hibernateSession = null;
FlushMode originalFlushMode = null;
try {
hibernateSession = this.entityManager.unwrap(Session.class);
originalFlushMode = hibernateSession.getFlushMode();
hibernateSession.setFlushMode(FlushMode.MANUAL);
// ...
}
finally {
if (hibernateSession != null) {
hibernateSession.setFlushMode(originalFlushMode);
}
}
}

Java exception - Can't call commit when autocommit=true

I am developing an application with EJB, when I make a transaction on the database I get the following error:
Grave: RAR5031:System Exception
javax.resource.spi.LocalTransactionException:
Can't call commit when autocommit=true
The following is the code:
public boolean update(Person vo) {
boolean r = false;
try
{
entityManager.getTransaction().begin();
entityManager.merge(vo);
entityManager.getTransaction().commit();
r = true;
} catch (Exception e)
{
} finally {
return r;
}
}
If this code does not invoke from an EJB not get error. I only get the error when I invoke from an EJB.
Put this annotation #TransactionManagement(TransactionManagementType.BEAN) on your session bean.
Example:
#TransactionManagement(TransactionManagementType.BEAN)
public class YourSessionBean ...
All EJB methods are transactional per default. The transaction handling is performed by the EJB and thus you dont need to invoke begin() and commit().

Hibernate connection problems

I'm working on a webapp and I have connection errors after Hibernate throws exceptions :
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
It gave me this exception each time I try to access my db after an exception occured.
I now Hibernate's not supposed to throw errors if my application is well coded but if something happens with the connection to the db, I don't want my application to be stuck with this error.
Here's my HibernateUtil class :
public class HibernateUtil {
private static Logger log = Logger.getLogger(HibernateUtil.class);
private static org.hibernate.SessionFactory sessionFactory;
private static String confFile = "hibernate-test.properties";
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
private HibernateUtil() {
}
public static void buildSessionFactory(){
Configuration configuration = new Configuration();
synchronized(HibernateUtil.class){
if(sessionFactory == null){
try {
Properties properties = new Properties();
properties.load(HibernateUtil.class.getClassLoader().getResourceAsStream(confFile));
configuration.setProperties(properties);
} catch (Exception e) {
log.fatal("cannot load the specified hibernate properties file: " + confFile);
throw new RuntimeException("cannot load the specified hibernate properties file : " + confFile, e);
}
sessionFactory = configuration.configure().buildSessionFactory();
}
HibernatePBEEncryptorRegistry registry = HibernatePBEEncryptorRegistry.getInstance();
if(registry.getPBEStringEncryptor("strongHibernateStringEncryptor") == null) {
StandardPBEStringEncryptor strongEncryptor = new StandardPBEStringEncryptor();
strongEncryptor.setAlgorithm("PBEWithMD5AndDES"); // not really needed as it is the default
strongEncryptor.setPassword("aStrongPassword");
registry.registerPBEStringEncryptor("strongHibernateStringEncryptor", strongEncryptor);
}
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null){
buildSessionFactory();
}
return sessionFactory;
}
public static Session getCurrentSession(){
if(!getSessionFactory().getCurrentSession().isOpen())
getSessionFactory().openSession();
return getSessionFactory().getCurrentSession();
}
}
Here's my BaseAction class where initialization and closing of sessions is set :
public class BaseAction extends ActionSupport {
public Session hib_session;
public void initHibSession() {
hib_session = HibernateUtil.getCurrentSession();
hib_session.beginTransaction();
hib_session.clear();
}
public void closeHibSession() {
hib_session.getTransaction().commit();
}
}
Here's an example of an action:
Transaction transaction = new Transaction(user, Transaction.Type.REGISTRATION, new HashSet(domains));
initHibSession();
hib_session.save(transaction);
closeHibSession();
transaction_id = transaction.getId();
Is there a way to avoid the exception above ?
It gave me this exception each time I try to access my db after an exception occurred.
I'm not sure to understand the exact condition. Anyway, after an exception, you should rollback the transaction, close the session and start over. That being said, I have some remarks about your code.
About your HibernateUtil:
why do you have a ThreadLocal, the Session#getCurrentSession() method handle that for you (you don't seem to use the thread local though).
in HibernateUtil.getCurrentSession(), why do you mess with getCurrentSession() and openSession()? Firstly, there is no need to do what you do, getCurrentSession() will return a new session if no session is associated to the current thread. Secondly, both approaches are different and have different semantics (you need to close the session yourself when using openSession()), you should use one or the other.
About your BaseAction:
I wonder why you clear() the session after Session#beginTransaction(). In case you didn't committed an ongoing transaction, you'll loose all the pending changes. Is this really what you want?
PS: I would consider using the Open Session in View pattern to remove all this burden from your code.
Resources
Sessions and transactions
Open Session in View

Categories