发生异常后继续进行交易-JPA

use*_*588 5 java spring hibernate jpa spring-transactions

我在Spring上使用JPA。我正在尝试批量导入。如果批量导入有问题,那么我想单独插入,如果也失败,那么我想保存到重复表。我为此编写了逻辑,但每次都会收到此错误:

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
Run Code Online (Sandbox Code Playgroud)

JPA的矿山设置如下:

@Bean(name = "dataSource", destroyMethod = "")
  public DataSource getDataSource() {
    return new JndiDataSourceLookup().getDataSource(props.getDbJndiName());
  }

  @Bean
  public JpaVendorAdapter getHibernateJpaVendorAdapter() {
    return new HibernateJpaVendorAdapter();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean getEntityManagerFactoryBean() {
    LocalContainerEntityManagerFactoryBean lcemfb = new LocalContainerEntityManagerFactoryBean();
    lcemfb.setDataSource(getDataSource());
    lcemfb.setPersistenceUnitName("MyPU");
    lcemfb.setPackagesToScan("com.project");
    lcemfb.setJpaVendorAdapter(getHibernateJpaVendorAdapter());
    lcemfb.setJpaProperties(getHibernateProperties());
    return lcemfb;
  }

  @Bean
  public Properties getHibernateProperties() {
    Properties jpaProperties = new Properties();
    jpaProperties.put(DIALECT, "org.hibernate.dialect.Oracle10gDialect");
    jpaProperties.put(SHOW_SQL, true);
    jpaProperties.put(AUTOCOMMIT, true);
    jpaProperties.put(FORMAT_SQL, true);
    jpaProperties.put(USE_SQL_COMMENTS, true);
    jpaProperties.put(STATEMENT_BATCH_SIZE, 20);
    jpaProperties.put(ORDER_INSERTS, true);
    jpaProperties.put("hibernate.ejb.entitymanager_factory_name", "MyEM");
    return jpaProperties;
  }

  @Bean
  public JpaTransactionManager getTransactionManager() {
    return new JpaTransactionManager(getEntityManagerFactoryBean().getObject());
  }

  @Bean
  public PersistenceExceptionTranslationPostProcessor getPersistenceExceptionTranslationPostProcessor() {
    return new PersistenceExceptionTranslationPostProcessor();
  }
Run Code Online (Sandbox Code Playgroud)

我得到这样的实体经理

@PersistenceContext(unitName = "MyPU")
  private EntityManager em;

  protected EntityManager em() {
    return em;
  }
Run Code Online (Sandbox Code Playgroud)

我的导入方法是:

  @Override
  @Transactional
  public void importBusinessFile(MultipartFile file)
      throws GeneralException, IOException {
    // process file

    //save batch
    dealsRepository.saveBatch(deals);
  }
Run Code Online (Sandbox Code Playgroud)

和来自存储库的saveBatch方法:

  public void saveBatch(List<Deal> list) {
    for (Deal deal : list) {
      em().persist(deal);
    }

    try {
      em().flush();
      em().clear();
    } catch (Exception e) {
      log.info("Duplicates detected, save individually.", e);

      for (Deal deal : list) {
        try {
          save(deal);
        } catch (Exception ex) {
          log.error("Problem saving individual deal", e);
          // TODO write to duplicates
        }
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

我尝试设置dontRollbackOn,但无法摆脱此异常。我发现了其他一些类似的线程,但是没有一个帮助我。

xyz*_*xyz 5

如果您的方法具有@Transactional批注,则方法内部发生任何异常都会将周围的事务标记为回滚。

您可以为@Transactional批注添加一个属性,以防止其回滚,例如:@Transactional(noRollbackFor = Exception.class)。所有运行时异常子类型的Spring回滚事务。

如果您想在捕获时执行某项操作,则应尝试在新事务中执行。但是要记住,不支持spring的自调用,您不能仅从method1调用transactional method2,而应从spring上下文当前服务获取并调用方法2。

PROPAGATION_NESTED使用具有多个保存点的单个物理事务,它可以回滚到该保存点。这种部分回滚允许内部事务作用域触发对其范围的回滚,尽管某些操作已被回滚,但外部事务仍能够继续物理事务。此设置通常映射到JDBC保存点,因此仅适用于JDBC资源事务。参见Spring的DataSourceTransactionManager。


简单的变体:

  @Autowired
  private ApplicationContext context.

  @Override
  @Transactional
  public void importBusinessFile(MultipartFile file)
      throws GeneralException, IOException {
    // process file

    try{
       dealsRepository.saveBatch(deals);
       //in  case fail-transaction for saveBatch is rollback main transactio is active
    }catch(Exception e){
        context.getBean(curent serivce).tryReSaveBatch(deals);
       //in  case fail - transaction for tryReSaveBatchis rollback , 
main transactio is active
    }
     // main transaction commited
  }

@Transactional(propagation = NESTED)
public void saveBatch(List<Deal> list) {
    for (Deal deal : list) {
      em().persist(deal);
    }
  }

@Transactional(propagation = NESTED)
public void tryReSaveBatch(List<Deal> list) {
for (Deal deal : list) {
        try {
          save(deal);
        } catch (Exception ex) {
          log.error("Problem saving individual deal", e);
          // TODO write to duplicates
        }
      }
  }
Run Code Online (Sandbox Code Playgroud)


use*_*588 1

我只能通过创建另一个包含批量导入方法的 bean 来解决这个问题。这样之后 Spring 就可以拦截来自该 bean 的调用并启动一个新事务。