简体   繁体   中英

Spring+Hibernate: Inserting mutliple records and @Transactional

I have an application built on Spring+Hibernate. I'm trying to insert data into the database from a list of entries in a file. The basic idea is to insert all the records which have valid data and log an error report for all the entries that haven't been inserted.

A service class, which is @Transactional, calls another service class to read the file and insert into the DB. At moment, I insert the records one after the other. Maybe I'll change this to Batch Processing. The insertion is executed in a try catch block.

The pseudo-code:

@Transactional
class MainService {     
     public void insertFromFile(File inputFile) {
          subServuce(toIterator(inputFile)); // toIterator(..) is just for understanding. 
     } 
}

class SubService {
    public void insert(Iterator input) {
        while (input.hasNext()) {
             try {
                 DBService.save(input.next());
             } catch (Exception e) {
                 // Log the error and continue with next records
             }
        }
    }
}

@Transactional
class DBService {
     public void save(Data input) {
          generalDao.save(input);
     }
}

Tried:

class SubService {
    public void insert(Iterator input) {
        while (input.hasNext()) {
             try {
                 save(input.next());
             } catch (Exception e) {
                 // Log the error and continue with next records
             }
        }
    }

    // @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
    @Transactional(propagation = Propagation.NOT_SUPPORTED, noRollbackFor = Exception.class)
    public void save(Data input) {
        DBService.save(input);
    }
}

Even though the exception is handled, the entire transaction is being rolled back and I get this error Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly

I tried specifying @Transactional(noRollBack = Exception.class) on SubService.insert(..) , that did not work.

The following are my questions:

  1. How do I tell Spring not to roll back and continue with the other records.
  2. Is it good practice to not to roll back while inserting multiple records? Doesn't this defy the Atomicity rule?
  3. In batch processing, how is this handled? If an error occurs are all inserted or none?

    Solution:

With the help of @Madhusudana Reddy Sunnapu's answer, was able to solve this.

Created a layer between SubService and DBService with the Propagation.REQUIRES_NEW. The issue was that even though SubService.save(..) is annotated with @Transactional, as it belongs to the same Bean, the proxy will not treat it as a different session.

SubServiceDao {
    // @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void save(Data input) {
        DBService.save(input);
    }
}

class SubService {
    public void insert(Iterator input) {
        while (input.hasNext()) {
             try {
                 SubServiceDao.save(input.next());
             } catch (Exception e) {
                 // Log the error and continue with next records
             }
        }
    }
}

Although you are catching the exception the Spring would still be able to detect that something has gone wrong with entityManager and in such cases it marks the transaction as rollback only.

This is because the entityManager Spring injects is a proxy that intercepts all the calls before delegating to real entityManager and gets it know that exception has been thrown by the real entityManager even if you catch it.

One option I see is you can start a new transaction on DBService.save(...) method @Transactional(propagation=Propagation.REQUIRES_NEW) . That way if any exception occurs due to invalid data it effects insertion of only that row and remaining will still continue.

From the Session javadoc:

If the Session throws an exception, the transaction must be rolled back and the session discarded. The internal state of the Session might not be consistent with the database after the exception occurs.

So, the entire transaction is rolled back and nothing remains inserted.

Do do validation (and necessary locking if needed) before inserting and retry the entire transaction in case of failure.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM