Spring Batch - 如何在处理器中同时处理多条记录?

时间:2016-05-09 19:21:44

标签: spring-batch

我有一个文件来解析和处理来自的记录。它可以逐行工作(一次解析一条记录)。我的要求是我要通过多行解析并从每个记录中获取所需的信息,然后在组合所有记录中的获取信息后,我调用服务来执行业务逻辑。我必须在我的Processor类中执行此逻辑。数据如下例所示:

001 123456 987654321551580 Wayne DR 1

001 123456 987654321552APT 786 1

001 123456 987654321553LOS ANGELES 1

001 123456 987654321554CA 1

001 123456 98765432155590001 1

我可以从每条记录中获取第30-32列的数据元素。在上面的例子中,每行中的值分别为551,552,553,554,555。他们都在文件中一起进来。所以基本上当我的处理器中的当前项解析第一行并发现它的'551'(在业务代码中表示地址行1)时,我想要获取该行后面的其余地址并将它们保存在一个完整的行中地址。最后,我想从处理器将此地址传递给服务类,然后继续解析文件中可用的下一条记录。我的问题是处理器逐行为每条记录工作,所以这样我就无法在所有这些相关的行之间保持跟踪/关联。 对不起,如果我无法以更简单的方式解释我的问题..我是Spring Batch的新手并且还在学习。

3 个答案:

答案 0 :(得分:5)

如果您知道关联的数据记录将在文件中彼此相邻(而不是随机展开),您可以利用SingleItemPeekableItemReader关联多行来创建一个完整的对象。这个older answer有更多信息。

示例上下文文件:

<bean id="peekingReader" class="com.package.whatever.YourPeekingReader">
    <property name="delegate" ref="flatFileItemReader"/>
</bean>

<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
    <property name="resource" value="file://temp/file.txt" />
    <property name="lineMapper">
        <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
            <property name="lineTokenizer" ref="yourTokenizer"/>
            <property name="fieldSetMapper" ref="yourMapper"/>
        </bean>
    </property>
</bean>

窥视阅读器示例:

public class YourPeekingReader extends SingleItemPeekableItemReader<YourObject> {

    @Override
    public YourObject read() {

        YourObject item = super.read();

        if (item == null) {
            return null;
        }

        while (true) {
            YourObject possibleRelatedObject = peek();
            if (possibleRelatedObject == null) {
                return item;
            }

            //logic to determine if next line in file relates to same object
            boolean matches = false; 

            if (matches) {
                item.addRelatedInfo(super.read());
            } else {
                return item;
            }
        }


    }

}

答案 1 :(得分:0)

@ Dean ..再次感谢。为了更准确地使用我的代码,这里是

客户记录-reader.xml

<batch:job id="myFileReaderJob">
    <batch:step id="stepA" next="stepSuccess">
        <batch:tasklet>        
        <batch:chunk reader="myInputReader" processor="myProcessor" writer="myWriter" commit-interval="1"/>
        </batch:tasklet>
    </batch:step>
    <batch:step id="stepSuccess">
        <batch:tasklet ref="successTasklet" />
    </batch:step>
</batch:job>

<bean id="myInputReader" scope="step" class="org.springframework.batch.item.file.FlatFileItemReader">
        <property name="lineMapper" ref="myLineMapper" />
</bean>

<bean id="myLineMapper" class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean id="fixedLengthLineTokenizer" class="org.springframework.batch.item.file.transform.FixedLengthTokenizer">
        <property name="names" value="custRecord,tranId,partyId,uniquePartyId,deNum,deVal" />
        <property name="columns" value="1-75,1-3,6-11,21-29,30-32,33-62" />
        <property name="strict" value="false" />
    </bean>
</property>
<property name="fieldSetMapper">
    <bean       class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
        <property name="prototypeBeanName" value="myInputData" />
    </bean>
</property>
</bean>

如您所见,我没有使用ItemReader的自定义实现来包装FlatFileItemReader。您能否详细详细说明如何在上面的代码中进行更改以实现SingleItemPeekableItemReader。

由于

答案 2 :(得分:0)

@Dean
I tried implementing as per your suggestion

Config1.xml
<import resource="classpath*:/META-INF/java-batchlauncher/mainConfig.xml" />

<batch:job id="prT813FileReaderJob">
        <batch:step id="stepA" next="stepB">
            <batch:tasklet ref="aTasklet" />
        </batch:step>
        <batch:step id="stepB" next="stepSuccess">
            <batch:tasklet>        
                <batch:chunk reader="prT813MultiReader" processor="participantRecordT813Processor" writer="prT813ItemWriter" commit-interval="1"/>
                <batch:listeners>
                    <batch:listener ref="enabledFeaturesStepListener"/>
                </batch:listeners>
                <batch:transaction-attributes propagation="NEVER"/> 
            </batch:tasklet>
        </batch:step>
        <batch:step id="stepSuccess">
            <batch:tasklet ref="successTasklet" />
        </batch:step>
    </batch:job>

My mainConfig.xml file changes:

<bean id="prT813MultiReader" scope="step" class="org.springframework.batch.item.file.MultiResourceItemReader">
        <property name="resources" value="#{jobParameters[INPUT_FILES]}" />
        <property name="delegate" ref="prT813InputReader" />
</bean>

<bean id="prT813MultiThreadedReader" scope="step" class="org.springframework.batch.item.file.MultiResourceItemReader">
        <property name="resources" value="#{stepExecutionContext[fileName]}" />
        <property name="delegate" ref="prT813InputReader" />
    </bean>

<bean id="prT813InputReader" scope="step" class="com.fileprocessing.ParticipantRecordT813ItemReader">
        <property name="delegate" ref="prT813CustomPeekableItemReader" />
    </bean>

    <bean id="prT813CustomPeekableItemReader" scope="step" class="org.springframework.batch.item.support.SingleItemPeekableItemReader">
        <property name="delegate" ref="participantRecordT813ItemReader" />
    </bean>

    <bean id="participantRecordT813ItemReader" scope="step" class="org.springframework.batch.item.file.FlatFileItemReader">
        <property name="lineMapper" ref="prT813LineMapper" />
    </bean>


Created a new Reader class:
public class ParticipantRecordT813ItemReader extends SingleItemPeekableItemReader<ParticipantRecordT813InputData> {

    private static final String CLASS = "ParticipantRecordT813ItemReader"; 

    @Override
    public ParticipantRecordT813InputData read() throws UnexpectedInputException, ParseException, Exception {

        ParticipantRecordT813InputData item = super.read();
        Log.report(CLASS, "I am in the reader ::::");
        if (item != null) {
            while (item.getDeNum()=="551") {
                Log.report(CLASS, "I am in the reader at DE551::::" + item.getDeNum());
                ParticipantRecordT813InputData possibleRelatedObject = peek();
                if (possibleRelatedObject == null) {
                    return item;
                }

                //logic to determine if next line in file relates to same object
                boolean matches = possibleRelatedObject.getDeNum()=="552"; 

                if (matches) {
                    Log.report(CLASS, "I am in the reader at DE552::::" + possibleRelatedObject.getDeNum());
                } else {
                    return item;
                }
            }
        }
        return item;
    }
}

I am getting the below exception:

ERROR [main] (AbstractStep.java:225)- Encountered an error executing step stepB in job prT813FileReaderJob
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.prT813MultiReader' defined in URL []: Initialization of bean failed; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert property value of type 'com.sun.proxy.$Proxy10 implementing org.springframework.batch.item.ItemStreamReader,org.springframework.batch.item.PeekableItemReader,java.io.Serializable,org.springframework.aop.scope.ScopedObject,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised' to required type 'org.springframework.batch.item.file.ResourceAwareItemReaderItemStream' for property 'delegate'; nested exception is java.lang.IllegalStateException: Cannot convert value of type [com.sun.proxy.$Proxy10 implementing org.springframework.batch.item.ItemStreamReader,org.springframework.batch.item.PeekableItemReader,java.io.Serializable,org.springframework.aop.scope.ScopedObject,org.springframework.aop.framework.AopInfrastructureBean,org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised] to required type [org.springframework.batch.item.file.ResourceAwareItemReaderItemStream] for property 'delegate': no matching editors or conversion strategy found







As you can see that prT813MultiReader and prT813MultiThreadedReader of type MultiResourceItemReader and I delegate them to prT813InputReader of type SingleItemPeekableItemReader.







I tried implementing ResourceAwareItemReaderItemStream in my reader class which get rid of the above exception but then it complaints on ParticipantRecordT813InputData item = super.read(); for nullPointerException. 

public class ParticipantRecordT813ItemReader extends SingleItemPeekableItemReader<ParticipantRecordT813InputData> implements ResourceAwareItemReaderItemStream<ParticipantRecordT813InputData> {

    private static final String CLASS = "ParticipantRecordT813ItemReader";
    SingleItemPeekableItemReader<ParticipantRecordT813InputData> delegate = new SingleItemPeekableItemReader<ParticipantRecordT813InputData>(); 

    @Override
    public ParticipantRecordT813InputData read() throws UnexpectedInputException, ParseException, Exception {

        ParticipantRecordT813InputData item = super.read();
        Log.report(CLASS, "I am in the reader ::::");
        if (item != null) {
            while (item.getDeNum()=="551") {
                Log.report(CLASS, "I am in the reader at DE551::::" + item.getDeNum());
                ParticipantRecordT813InputData possibleRelatedObject = peek();
                if (possibleRelatedObject == null) {
                    return item;
                }

                //logic to determine if next line in file relates to same object
                boolean matches = possibleRelatedObject.getDeNum()=="552"; 

                if (matches) {
                    Log.report(CLASS, "I am in the reader at DE552::::" + possibleRelatedObject.getDeNum());
                } else {
                    return item;
                }
            }
        }
        return item;
    }

    @Override
    public void close() throws ItemStreamException {
        // TODO Auto-generated method stub
        super.close();
    }

    @Override
    public void open(ExecutionContext arg0) throws ItemStreamException {
        // TODO Auto-generated method stub
        super.open(arg0);
    }

    @Override
    public void update(ExecutionContext arg0) throws ItemStreamException {
        // TODO Auto-generated method stub
        super.update(arg0);
    }

    @Override
    public void setResource(Resource arg0) {
        // TODO Auto-generated method stub
        super.setDelegate(delegate);        
    }
}


Any idea where I am wrong????