Spring+Hibernate:插入多条记录和@Transactional

Spring+Hibernate: Inserting mutliple records and @Transactional

我有一个基于 Spring+Hibernate 的应用程序。我正在尝试将文件中的条目列表中的数据插入数据库。基本思想是插入所有具有有效数据的记录,并为所有未插入的条目记录错误报告。

服务class,即@Transactional,调用另一个服务class读取文件并插入数据库。此刻,我一个接一个地插入记录。也许我会把它改成批处理。插入在 try catch 块中执行。

伪代码:

@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);
     }
}

尝试过:

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);
    }
}

即使处理了异常,整个事务都在回滚,我得到这个错误Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly

我尝试在 SubService.insert(..) 上指定 @Transactional(noRollBack = Exception.class),但没有用。

以下是我的问题:

  1. 如何告诉 Spring 不要回滚并继续处理其他记录。
  2. 插入多条记录时不回滚是好习惯吗?这不是违背了原子性规则吗?
  3. 在批处理中,这个是怎么处理的?如果出现错误全部插入或者none?

    解法:

在@Madhusudana Reddy Sunnapu 的回答的帮助下,能够解决这个问题。

使用 Propagation.REQUIRES_NEW 在 SubService 和 DBService 之间创建了一个层。问题是即使 SubService.save(..) 被 @Transactional 注释,因为它属于同一个 Bean,代理不会将其视为不同的会话。

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
             }
        }
    }
}

来自 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.

因此,整个事务被回滚,没有任何内容被插入。

在插入之前进行验证(如果需要,还需要进行必要的锁定),并在失败的情况下重试整个事务。

尽管您正在捕获异常,Spring 仍然能够检测到 entityManager 出现了问题,在这种情况下,它会将事务标记为仅回滚。

这是因为 entityManager Spring 注入是一个代理,它在委托给真正的 entityManager 之前拦截所有调用,并让它知道真正的 entityManager 抛出了异常 entityManager 抓到就算了

我看到的一个选项是您可以在 DBService.save(...) 方法 @Transactional(propagation=Propagation.REQUIRES_NEW) 上开始新事务。这样,如果由于无效数据而发生任何异常,它只会影响该行的插入,其余的仍将继续。