Spring批处理读取key =值并将其加载到DB中

时间:2016-04-27 08:37:31

标签: spring-batch

我是春季批处理新手,并且有一个包含.txt格式的密钥参数值的Feed文件。我需要使用spring batch将文件加载到Mysql DB中。有没有办法读取带有键值消息的文本文件。两行由空行分隔,分隔符为' ='。

示例文件:

Name=Jack
Id=ADC12345
ClassId=7018
Rank=-326

Name=Gile
Id=FED12345
ClassId=7018
Rank=-32

名称,ID,ClassId和Rank是列值。

2 个答案:

答案 0 :(得分:1)

这是一个有效的解决方案(你只需要在最后一条记录后面留一个空行或者不会被读取):

1)声明您的业务对象:

public class Student {

    private String name;
    private String id;
    private Integer classId;
    private Integer rank;

    // Getter + Setters

}

2)声明一个自定义的itemstreamreader,您将委派实际的FlatFileItemReader:

public class CustomMultiLineItemReader implements ItemStreamReader<Student> {

    private FlatFileItemReader<FieldSet> delegate;

    @Override
    public void open(ExecutionContext executionContext) throws ItemStreamException {
        delegate.open(executionContext);
    }

    @Override
    public void update(ExecutionContext executionContext) throws ItemStreamException {
        delegate.update(executionContext);
    }

    @Override
    public void close() throws ItemStreamException {
       delegate.close();
    }

    // Getter + Setters
}

3)覆盖其read方法以手动映射多行记录:

public Student read() throws Exception {
    Student s = null;

    for (FieldSet line = null; (line = this.delegate.read()) != null;) {

        if (line.getFieldCount() == 0) {
            return s; // Record must end with footer
        } else {

            String prefix = line.readString(0);
            if (prefix.equals("Name")) {
                s = new Student(); // Record must start with header
                s.setName(line.readString(1));
            }
            else if (prefix.equals("Id")) {
               s.setId(line.readString(1));
            }
            else if (prefix.equals("ClassId")) {
                s.setClassId(line.readInt(1));
            }
            else if (prefix.equals("Rank")) {
                s.setRank(line.readInt(1));
            }
        }
    }
   return null;
}

4)在步骤中声明读者并配置它:

<bean class="xx.xx.xx.CustomMultiLineItemReader">
    <property name="delegate">
        <bean class="org.springframework.batch.item.file.FlatFileItemReader">
            <property name="resource" value="file:${YOUR_FILE}"></property>
            <property name="linesToSkip" value="0"></property>
            <property name="lineMapper">
                <bean class="org.springframework.batch.item.file.mapping.PatternMatchingCompositeLineMapper">
                    <property name="tokenizers">
                        <map>
                            <entry key="*">
                                <bean class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
                                    <property name="delimiter" value="="></property>
                                </bean>
                            </entry>                                                   
                        </map>
                    </property>
                    <property name="fieldSetMappers">
                        <map>
                            <entry key="*">
                                 <bean class="org.springframework.batch.item.file.mapping.PassThroughFieldSetMapper" />
                            </entry>
                        </map>
                    </property>
                </bean>
            </property>
        </bean>
    </property>
</bean>

我使用PatternMatchingCompositeLineMapper将行内容(此处为:*)与相应的lineTokenizer和lineMapper相关联(即使在这种情况下它没用)。

然后,PassThroughFieldSetMapper让读者进行映射,DelimitedLineTokenizer在“=”字符上拆分行。

答案 1 :(得分:0)

此输入格式存在两个挑战

  1. 开始/结束完整项目
  2. 按键/值对分割项目
  3. 一种解决方案可能是使用自定义RecordSeparatorPolicy和自定义LineMapper,如

    import java.util.HashMap;
    import java.util.Map;
    
    import org.junit.Test;
    import org.springframework.batch.item.ExecutionContext;
    import org.springframework.batch.item.file.FlatFileItemReader;
    import org.springframework.batch.item.file.mapping.DefaultLineMapper;
    import org.springframework.batch.item.file.mapping.FieldSetMapper;
    import org.springframework.batch.item.file.separator.RecordSeparatorPolicy;
    import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
    import org.springframework.batch.item.file.transform.FieldSet;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.validation.BindException;
    
    public class ReaderKeyValueTest {
        @Test
        public void test() throws Exception {
            FlatFileItemReader<Map<String, String>> reader = new FlatFileItemReader<Map<String, String>>();
            reader.setResource(new ClassPathResource("keyvalue.txt"));
            // custom RecordSeparatorPolicy
            reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() {
    
                @Override
                public String preProcess(final String record) {
                    // empty line is added to the previous 'item'
                    if (record.isEmpty()) {
                        return record;
                    } else {
                        // line with content means it is part of an 'item', lets enhance it with adding a separator
                        return record + ",";
                    }
                }
    
                @Override
                public String postProcess(final String record) {
                    return record;
                }
    
                @Override
                public boolean isEndOfRecord(final String record) {
                    // the end of a record is marked with the last key/value pair for "Rank"
                    if (record.contains("Rank=")) {
                        return true;
                    } else {
                        return false;
                    }
                }
            });
            DefaultLineMapper<Map<String, String>> lineMapper = new DefaultLineMapper<Map<String, String>>();
            // the key/value pairs are separated with ',', so we can use the standard DelimitedLineTokenizer here
            lineMapper.setLineTokenizer(new DelimitedLineTokenizer());
            lineMapper.setFieldSetMapper(new FieldSetMapper<Map<String, String>>() {
    
                @Override
                public Map<String, String> mapFieldSet(final FieldSet fieldSet) throws BindException {
                    Map<String, String> item = new HashMap<String, String>();
                    // split each "Key=Value" and add to the Map
                    for (int i = 0; i < fieldSet.getValues().length; i++) {
                        String[] entry = fieldSet.getValues()[i].split("=");
                        item.put(entry[0], entry[1]);
                    }
                    return item;
                }
            });
            reader.setLineMapper(lineMapper);
            reader.open(new ExecutionContext());
            Map<String, String> item;
            while ((item = reader.read()) != null) {
                System.out.println(item.toString());
            }
            reader.read();
            reader.close();
        }
    }
    

    sysout产生

    {ClassId=7018, Id=ADC12345, Name=Jack, Rank=-326}
    {ClassId=7018, Id=FED12345, Name=Gile, Rank=-32}