Asynchronous (non-blocking) Execution in JDBC, Hibernate or Spring?
Join the DZone community and get the full member experience.
Join For FreeThere is no so-called asynchronous execution support in JDBC mainly
because most of the time you want to wait for the result of your DML or DDL, or because there is too much complexity involved between the
back-end database and the front end JDBC driver.
Some database vendors do provide such support in their native drives.
For example Oracle supports non-blocking calls in its native OCI driver.
Unfortunately it is based on polling instead of callback or interrupt.
Neither Hibernate or Spring supports this feature.
But sometimes you do need such a feature. For example some business
logic is still implemented using legacy Oracle PL/SQL stored procedures
and they run pretty long. The front-end UI doesn't want to wait for its
finish and it just needs to check the running result later in a database
logging table into which the store procedure will write the execution
status.
In other cases your front-end application really cares about low latency
and doesn't care too much about how individual DML is executed. So you
just fire a DML into the database and forget the running status.
Nothing can stop you from making asynchronous DB calls using
multi-threading in your application. (Actually even Oracle recommends to
use multi-thread instead of polling OCI for efficiency).
However you must think about how to handle transaction and connection (or Hibernate Session) in threads.
Before continuing, let's assume we are only handling local transaction instead of JTA.
1. JDBC
It is straightforward. You just create another thread (DB thread
hereafter) from the calling thread to make the actual JDBC call.
If such a call is frequent, you call use ThreadPoolExecutor to reduce thread's creation and destroy overhead.
2. Hibernate
You usually use session context policy "thread" for Hibernate to automatically handle your session and transaction.
With this policy, you get one session and transaction per thread. When
you commit the transaction, Hibernate automatically closes the session.
Again you need to create a DB thread for the actual stored procedure call.
Some developer may be wondering whether the new DB thread inherits its parent calling thread's session and transaction.
This is an important question. First of all, you usually don't want to
share the same transaction between the calling thread and its spawned DB
thread because you want to return immediately from the calling thread
and if both threads share the same session and transaction, the calling
thread can't commit the transaction and long running transaction should
be avoided.
Secondly Hibernate's "thread" policy doesn't support such inheritance because if you look at Hibernate's corresponding ThreadLocalSessionContext, it is using ThreadLocal class instead of InheritableThreadLocal.
Here is a sample code in the DB thread:
// Non-managed environment and "thread" policy is in place // gets a session first Session sess = factory.getCurrentSession(); Transaction tx = null; try { tx = sess.beginTransaction(); // call the long running DB stored procedure //Hibernate automatically closes the session tx.commit(); } catch (RuntimeException e) { if (tx != null) tx.rollback(); throw e; }
3.Spring's Declarative Transaction
Let's suppose your stored procedure call is included in method:
@Transactional(readOnly=false) public void callDBStoredProcedure();
The calling thread has the following method to call the above method asynchronously using Spring's TaskExecutor:
@Transactional(readOnly=false) public void asynchCallDBStoredProcedure() { //creates a DB thread pool this.taskExecutor.execute(new Runnable() { @Override public void run() { //call callDBStoredProcedure() } }); }
You usually configure Spring's HibernateTransactionManager and the default proxy mode (aspectj is another mode) for declarative transactions. This class binds a transaction and a Hibernate session to each thread and doesn't Inheritance either just like Hibernate's "thread" policy.
So you must put callDBStoredProcedure() in a different class as a Spring's bean so that the DB thread in method asynchCallDBStoredProcedure() can load that bean's AOP proxy and call callDBStoredProcedure() through that proxy.
About The Author
Opinions expressed by DZone contributors are their own.
Comments