2017-07-03 75 views
1

我正在使用JPA和Spring。我正在嘗試批量導入。如果批量導入有問題,那麼我想單獨插入,如果這也失敗了,那麼我想保存到複製表。我寫了這樣的邏輯,但我得到這個錯誤每次:異常後繼續事務 - JPA

Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly 

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

我得到這樣

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

    protected EntityManager em() { 
    return em; 
    } 

我進口經營單位經理方法是:

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

    //save batch 
    dealsRepository.saveBatch(deals); 
    } 

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

我試着設置dontRollbackOn,但我無法過去這個異常。我發現了一些其他類似的線程,但沒有人幫助我。

+0

我不知道spring在這裏,但我認爲它在這裏與EJB容器相似,因此它是一個事務攔截器,捕獲並重新拋出異常並將事務標記爲回滾。這主要發生在相應標記的未檢查異常或檢查異常。如果你不能改變異常或者阻止攔截器的響應,你可以嘗試使用「嵌套」事務(不是真正嵌套的,而是有些,即暫停正在運行的事務併爲了寫入而啓動一個新事務)。 – Thomas

回答

0

我只能設法通過創建一個包含批量導入方法的另一個bean來解決這個問題。所以在Spring之後,可以攔截來自這個bean的調用並開始一個新的事務。

1

如果您的方法具有@Transactional註釋,則在您的方法內發生任何異常都會將周圍的事務標記爲回滾,即使您發現異常。

您可以爲@Transactional註釋添加一個屬性,以防止它回滾如下:@Transactional(noRollbackFor = Exception.class)。所有子類型運行時異常的Spring回滾事務。

如果你想要做什麼,當你趕上你應該嘗試在新的交易中做到這一點。但請記住,在春季自我調用不支持,你不能只從method1調用事務方法2,你應該從春天的上下文當前服務和呼叫方法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 
     } 
     } 
    } 
+0

感謝您的回答。是的,我正在使用** @ Transactional **,正如你從上面的代碼中看到的那樣。我已經嘗試使用dontRollbackOn,當spring ** @ Transactional **註釋和noRollbackFor使用javax.Transactional註釋時,但它們都不適用於我。 你能給我一個例子如何獲得春季上下文當前服務,所以我可以做額外的電話。 – user1403588

+0

我添加了簡單的變體 – xyz

+0

感謝您的回答。不幸的是Propagation.NESTED沒有工作,因爲我得到了jpa不支持的異常。但使用你的答案,我發現了一些新的搜索詞,這也是一個醜陋的解決方案。我將批量導入移動到新的Bean(@Repository)中,之後代碼開始工作。 – user1403588