HibernateTransactionManager @Transactional(propagation=REQUIRES_NEW) cannot open 2 sessions

Viewed 439

There is one batch job looking like this:

@Transactional
public void myBatchJob() {
    // retrieves thousands of entries and locks them 
    // to prevent other jobs from touthing this dataset
    entries = getEntriesToProcessWithLock(); 
    additional = doPrepWork(); // interacts with DB
    processor = applicationContext.getBean(getClass());
    while (!entries.isEmpty()) {
        result = doActualProcessing(entries, additional); // takes as many entries as it needs; removes them from collection afterwards
        resultDao.save(result);
    }
}

However I occasionally get the below error if the entries collection is big enough.

ORA-01000: maximum open cursors exceeded

I decided to blame doActualProcessing() and save() methods as they could end up in creating hundreds of blobs in one transaction.

The obvious way out seems to be splitting processing into multiple transactions: one for getting and locking entries and multiple other transactions for processing and persisting. Like this:

@Transactional
public void myBatchJob() {
    // retrieves thousands of entries and locks them 
    // to prevent other jobs from touthing this dataset
    entries = getEntriesToProcessWithLock(); 
    additional = doPrepWork(); // interacts with DB
    processor = applicationContext.getBean(getClass());
    while (!entries.isEmpty()) {
        processor.doProcess(entries, additional);
    }
}

@Transactional(propagation=REQUIRES_NEW)
public void doProcess(entries, additional) {
    result = doActualProcessing(entries, additional); // takes as many entries as it needs; removes them from collection afterwards
    resultDao.save(result);
}

and now whenever doProcess is called I get:

Caused by: org.hibernate.HibernateException: illegally attempted to associate a proxy with two open Sessions

How do I make HibernateTransactionManager do what REQUIRES_NEW javadoc suggests: suspend current transaction and start a new one?

1 Answers

In my opinion the problem lies in the fact that you have retrieved the entities in the top Transaction and while they are still associated with that transaction you try to pass them (proxies) to method which would be processed in a separate transaction.

I think that you could try two options:

1) Detach the entities before ivoking processor.doProcess(entries, additional);:

session.evict(entity); // loop through the list and do this

then inside inner transaction try to merge:

session.merge(entity);

2) Second option would be to retrieve ids instead of entities in the getEntriesToProcessWithLock. Then you would pass plain primitive fields which wont cause proxy problems. You would then retrieve proper entities inside of the inner transaction.

Related