3
votes

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?

2

2 Answers

1
votes

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.

3
votes

Saving 1000 entities shouldn't take that long. Make sure you benefit from JDBC batching:

  1. You need to use an Entity identifier that's not IDENTITY. IDENTITY disabled batching. SEQUENCE and TABLE generators allow batching.
  2. 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"/>
    
  3. If you want to timeout the saving operation you need to annotate your service method with:

This way after 30s your saving transaction will timeout and an exception will be thrown, rolling back all your inserts.


Batch processing

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:

  • process batches concurrently
  • stop all batch processing at once, using shutdownNow

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