我在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 :(得分:3)
如果您的方法具有@Transactional注释,则方法内发生的任何异常都会将周围的事务标记为回滚。
您可以为@Transactional注释添加属性,以防止其回滚,如: @Transactional(noRollbackFor = Exception.class)。所有子类型的运行时异常的Spring回滚事务。
如果你想抓住什么东西,你应该尝试在新的交易中做。但是要记住春天的自我调用不受支持,你不能从方法1调用事务方法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
}
}
}
答案 1 :(得分:0)
我只是通过创建另一个包含批量导入方法的bean来解决这个问题。所以Spring之后可以拦截来自这个bean的调用并开始一个新的事务。