Is it possible to use a transaction in another thread?
Like pass a transaction created in thread A and then execute some logic in Thread B within the same transaction?
I have two queues and separate executors which handles population of certain Entity types.
However, a batch job is managing both population and waiting for each to finish. It would be unnecessary to create two transactions. If one fails, ideally I'd want all data to be rolled back so it would have been ideal to run them as one transaction as well as it provides improved performance.
So, is it possible to create one transaction, pass it along to another thread, execute some stuff within the boundaries of the first one?
I am using Spring and Hibernate and currently using
TransactionTemplate template = new TransactionTemplate( getTransactionManager() );
template.setPropagationBehavior(propagationBehavior);
template.setReadOnly(readOnly);
TransactionStatus status = getTransactionManager().getTransaction(template);
to create a transaction, not using annotations at all and have no plans to do so either.
It's not possible with Spring. All the transaction based code eventually ends up in TransactionSynchronizationManager
which is full of ThreadLocal
s and no way to copy those values from one thread to another.
If you want to be able to do this, you can use Spring to get the DataSource
but you have to create your own connections and manually create Hibernate sessions. Spring's PlatformTransactionManager
is out of the question.
[EDIT] What's the point of having several threads in your case? If you want to parallelize work, then the correct approach is to have N threads which prepare the data that should be inserted into the database and then a single thread which creates 1 transaction and then does all the work.
Yes, that means you need to keep a lot of data in memory.
If you really don't want to do that, then the next solution is to have work tables where each thread puts their results. When both threads finish, you start another thread which locks the work tables and runs a few SQL queries to copy the data to the correct place.
Always keep in mind that Database connections, SQL and threads don't mix. A database is global state. If you change global state from several places at the same time, you'll always face all kind of odd problems. Try to avoid that. Split the work into many small, independent tasks (i.e. which work perfectly fine when each of them has their own transaction). If you can't do that, you need to rethink your design until you can or you can't use threads.
In Spring, it is actually possible to transfer the transactional context from one thread to another with a simple trick as below:
org.springframework.orm.hibernate4.SessionHolder
should be enough as it also keeps the link to the ongoing transaction.org.springframework.transaction.support.TransactionSynchronizationManager.bindResource(Object, Object)
. After this, the second thread should be able to work in the same Hibernate session and transaction as its parent.There might be subtle detail that we need to care but the following code should get the job done:
final SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
Runnable newTask = new Runnable() {
@Override
public void run() {
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
// Business stuff here.
}
};
Executors.newFixedThreadPool(1).submit(newTask);
Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With