I am working with JPA 2 Hibernate implementation and Spring MVC to manage transactions
I am using Generic DAO pattern to manage database operations
Currently, we have a transaction that is a single method, something like:
someDao.save (someObject)
It is a @Transactional
method invoked from a JSF page.
The method save is inherited from Generic DAO, it persists to database many entities with cascade oneToMany relationships, but it has to execute some 1000s of insert statements in the database, so it takes a very long time.
I'd like to offer a way to the user to cancel the operation, but so far, I'm unable to do that, when I call the method save(), it cannot be canceled till all the inserts have been executed.
I tried to throw some exceptions, to rollback transaction from injected PersistenceContext, but it's useless, it just finishes with no error :(
Obviously, I cannot throw an exception from within the method, it's just a single call from my source. JPA PersistenceContext does everything
Is there a way to cancel the operation and force it to rollback?
Saving 1000 entities shouldn't take that long. Make sure you benefit from JDBC batching:
Set the following hibernate properties:
<property name="hibernate.order_updates" value="true"/>
<property name="hibernate.order_inserts" value="true"/>
<property name="hibernate.jdbc.batch_versioned_data" value="true"/>
<property name="hibernate.jdbc.fetch_size" value="50"/>
<property name="hibernate.jdbc.batch_size" value="520"/>
If you want to timeout the saving operation you need to annotate your service method with:
For Spring
@Transactional(timeout = 30)
For Java EE
UserTransaction.setTransactionTimeout(30)
This way after 30s your saving transaction will timeout and an exception will be thrown, rolling back all your inserts.
If you want to cancel the batch insert operation while allowing the already processed items to proceed, then you need to opt for batch processing.
You need to split all items into chunks and insert them in batches. For that I'd split the batch-start and batch-end boundaries from the actual batch processing thread. So while the batch-start and batch-end are triggered by a web request, the batch processing is run by a dedicated ExecutorService.
This way you can:
Because the query executions are I/O blocking operations, you need to support Thread interruptions so when the batch-end thread interrupts the batch processing executors, you could be notified to stop processing the next batch and exit as soon as possible.
I'd use a separate ExecutionService
maybe this will help you:
http://docs.jboss.org/hibernate/orm/3.5/javadocs/org/hibernate/Session.html#cancelQuery()
HibernateSession provides an cancelQuery-Method, which can be used to cancel the current query.
Note: It can take some amount of time, before the query really terminates.
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