不在Spring批处理中跳过异常

时间:2013-08-24 09:26:05

标签: spring-batch

Spring批处理无法跳过异常。任何人都可以建议我在这里错过了什么吗?

<batch:job id="runPromotion">   
        <batch:step id="readPromotionStep">
            <batch:tasklet ref="processPromotion"></batch:tasklet>
            <batch:next on="SUCCESS" to="getPromotionalPoints" />
            <batch:end on="FAILED"></batch:end> 
            <batch:listeners>
                <batch:listener ref="queryProvider"/>           
            </batch:listeners>          
        </batch:step>
        <batch:step id="getPromotionalPoints">      
            <batch:tasklet >            
                <batch:chunk reader="transactionDataReader" writer="userPromotionWriter" commit-interval="100" skip-limit="100">                
                <batch:skippable-exception-classes >
                    <batch:include class="org.hibernate.exception.ConstraintViolationException"/>
                    <batch:include class="javax.persistence.PersistenceException"/>
                </batch:skippable-exception-classes>
                </batch:chunk>
            </batch:tasklet>
            <batch:listeners>
                <batch:listener ref="queryProvider"/>           
            </batch:listeners>


        </batch:step>   

    </batch:job>
运行批处理时

以下是错误:

  

2013-08-24 14:43:16,451 - [main] WARN:org.hibernate.engine.jdbc.spi.SqlExceptionHelper:143 - SQL错误:1062,SQLState:23000   2013-08-24 14:43:16,451 - [main]错误:org.hibernate.engine.jdbc.spi.SqlExceptionHelper:144 - 键'promotion_id'重复输入'1'   javax.persistence.PersistenceException:org.hibernate.exception.ConstraintViolationException:键'promotion_id'的重复条目'1'           在org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)

1 个答案:

答案 0 :(得分:0)

如果您查看错误

  

javax.persistence.PersistenceException:org.hibernate.exception.ConstraintViolationException

您会注意到, PersistenceException 是引发的实际错误,而 ConstraintViolationException 是原因。这就是为什么向 skippable-exception-classes 中添加 ConstraintViolationException 无效的原因,但令我惊讶的是,甚至 PersistenceException 都不适合您。它对我有用(但是我顺便使用了Java配置)。此外,由于您还将 PersistenceException (这是 ConstraintViolationException 的超类)添加到 skippable-exception-classes ,因此添加了 ConstraintViolationException 似乎是多余的。

我假设您只希望跳过 ConstraintViolationException 。我尝试了两种解决方案(但使用Java配置):

  1. 实施SkipPolicy以创建自定义的船长。注意我们如何检查引发异常的原因是否是 ConstraintViolationException

    package pkg;
    
    import javax.persistence.PersistenceException;
    import org.hibernate.exception.ConstraintViolationException;
    import org.springframework.batch.core.step.skip.SkipLimitExceededException;
    import org.springframework.batch.core.step.skip.SkipPolicy;
    import org.springframework.stereotype.Component;
    
    public class ConstraintViolationExceptionSkipper implements SkipPolicy {
    
        private int skipLimit;
    
        @Override
        public boolean shouldSkip(Throwable t, int skipCount) throws SkipLimitExceededException {
          if(t instanceof PersistenceException && 
              t.getCause() instanceof ConstraintViolationException) {
            return true;
          }
          return false;
        }
    
        public void setSkipLimit(int skipLimit) {
          this.skipLimit = skipLimit;
        }
    }
    

    并将上面的船长设置为块中的 skip-policy ,并摆脱 skippable-exception-class 块。还要注意在此处设置 skip-limit 的方法:

    <batch:chunk reader="transactionDataReader" writer="userPromotionWriter" commit-interval="100" 
        skip-policy="skipPolicy" />
    
    <bean id="skipPolicy" class=pkg.ConstraintViolationExceptionSkipper">
      <property name="skipLimit" value="100" />
    </bean>
    
  2. 修改您的编写器以捕获PersistenceException并将其作为ConstraintVioaltionException重新抛出。我不确定您是如何实现 userPromotionWriter 的,但是下面是一个示例实现。它使用JPA进行持久化,显式刷新EntityManager并捕获并引发一个新异常,如我刚才所说:

    package pkg;
    
    import java.util.List;
    
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    import org.hibernate.exception.ConstraintViolationException;
    import org.springframework.batch.item.ItemWriter;
    import org.springframework.stereotype.Component;
    
    @Component("userPromotionWriter")
    public class UserPromotionWriter implements ItemWriter<User> {
    
      @PersistenceContext
      private EntityManager entityManger;
    
      @Override
      public void write(List<? extends User> users) throws Exception {
          for(User user : users) {
            entityManger.persist(user);
            try {
                entityManger.flush();
            } catch(Exception e) {
                if(e.getCause() instanceof ConstraintViolationException) {
                    throw new ConstraintViolationException("Tried to insert a User record with a non-unique user_id of " + user.getUserId(), null, null);
                }
                throw e;
            }
            finally {
              entityManger.clear();
            }
          }
       }
    }
    

    现在,由于抛出的异常是ConstraintViolationException(而不是PersistenceException),因此以下方法可以正常工作:

    <batch:chunk reader="transactionDataReader" writer="userPromotionWriter" commit-interval="100" skip-limit="100"> <batch:skippable-exception-classes > <batch:include class="org.hibernate.exception.ConstraintViolationException"/> </batch:skippable-exception-classes> </batch:chunk>