Spring batch 如何使用BeanRapperFieldSetMapper映射字段子集?

Spring batch 如何使用BeanRapperFieldSetMapper映射字段子集?,spring-batch,Spring Batch,我有一个Spring批处理应用程序,其中使用beanwrapperfiedsetmapper使用原型对象映射字段。但是,正在读取的CSV文件(通过FlatFileItemReader)包含一个(指示符)字段,用于确定另一个字段的映射。如果指示器字段的值为Y,则另一个字段的值应映射到属性foo,否则应映射到属性bar 我知道我可以使用自定义的FieldSetMapper来实现这一点,但是我必须对所有其他字段(其中有很多字段)的映射进行编码。或者,我可以通过ItemProcessor进行后期阅读,但

我有一个Spring批处理应用程序,其中使用
beanwrapperfiedsetmapper
使用原型对象映射字段。但是,正在读取的CSV文件(通过
FlatFileItemReader
)包含一个(指示符)字段,用于确定另一个字段的映射。如果指示器字段的值为Y,则另一个字段的值应映射到属性
foo
,否则应映射到属性
bar

我知道我可以使用自定义的
FieldSetMapper
来实现这一点,但是我必须对所有其他字段(其中有很多字段)的映射进行编码。或者,我可以通过
ItemProcessor
进行后期阅读,但是我的域(原型)对象必须有一个表示指示符字段的属性(我不喜欢这样做,因为它实际上不是业务域的一部分)

是否可以使用自定义的
FieldSetMapper
仅映射这些自定义字段,并将其他映射委托给
beanwrapperfiedsetmapper
?还是有其他更好的方法来解决这个问题

下面是我当前尝试使用自定义的
FieldSetMapper
并委托给
beanwrapperfiedsetmapper

public class DelegatedFieldSetMapper extends BeanWrapperFieldSetMapper<MyProtoClass> {

    @Override
    public MyProtoClass mapFieldSet(FieldSet fieldSet) throws BindException {       
        String indicator = fieldSet.readString("indicator");
        Properties fieldProperties = fieldSet.getProperties();

        if (indicator.equalsIgnoreCase("y")) {
            fieldProperties.put("test.foo", fieldSet.readString("value");
        } else {
            fieldProperties.put("test.bar", fieldSet.readString("value");
        }

        fieldProperties.remove("indicator");
        Set<Object> keys = fieldProperties.keySet();
        List<String> names = new ArrayList<String>();
        List<String> values = new ArrayList<String>();      
        for (Object key : keys) {
            names.add((String) key);
            values.add((String) fieldProperties.getProperty((String) key));
        }
        DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(names.toArray(new String[names.size()]), values.toArray(new String[values.size()]));     
        return super.mapFieldSet(domainObjectFieldSet);
    }

}

编写自己的
FieldSetMapper
,其中包含一组准备好的学员。
这些代理是为每种不同类型的字段映射预先构建的。
在对象路由中,根据指示符字段(例如,使用a)更正代理。

我看不到任何其他方法,但是这个解决方案非常容易维护。

假设BatchWorkObject是要映射的类。 这里是一个Spring引导风格的示例代码,只需要添加自定义逻辑

   new BeanWrapperFieldSetMapper<BatchWorkObject>(){
       {
        this.setTargetType(BatchWorkObject.class);
        }

    @Override
        public BatchWorkObject mapFieldSet(FieldSet fs)
                throws BindException {
            BatchWorkObject tmp= super.mapFieldSet(fs);
            // your custom code here
            return tmp;
        }

    });
new beanwrapperfiedsetmapper(){
{
此.setTargetType(BatchWorkObject.class);
}
@凌驾
公共BatchWorkObject映射字段集(字段集fs)
抛出绑定异常{
BatchWorkObject tmp=super.mapFieldSet(fs);
//您的自定义代码在这里
返回tmp;
}
});

除了一个导致FlatFileParseException的问题外,该代码实际上实现了所需的功能。
DelegatedFieldSetMapper
包含以下问题:

@Configuration
@EnableBatchProcessing
public class BatchConfiguration { 

    @Value("${file}")
    private File file;

    @Bean
    @Scope("prototype")
    public MyProtoClass () {
        return new MyProtoClass();
    }

    @Bean
    public ItemReader<MyProtoClass> reader(LineMapper<MyProtoClass> lineMapper) {
        FlatFileItemReader<MyProtoClass> flatFileItemReader = new FlatFileItemReader<MyProtoClass>();
        flatFileItemReader.setResource(new FileSystemResource(file));
        final int NUMBER_OF_HEADER_LINES = 1;
        flatFileItemReader.setLinesToSkip(NUMBER_OF_HEADER_LINES);
        flatFileItemReader.setLineMapper(lineMapper);
        return flatFileItemReader;
    }

    @Bean 
    public LineMapper<MyProtoClass> lineMapper(LineTokenizer lineTokenizer, FieldSetMapper<MyProtoClass> fieldSetMapper) {
        DefaultLineMapper<MyProtoClass> lineMapper = new DefaultLineMapper<MyProtoClass>();
        lineMapper.setLineTokenizer(lineTokenizer);
        lineMapper.setFieldSetMapper(fieldSetMapper);
        return lineMapper;
    }

    @Bean
    public LineTokenizer lineTokenizer() {
        DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();        
        lineTokenizer.setNames(new String[] {"value", "test.bar", "test.foo", "indicator"});
        return lineTokenizer;
    }

    @Bean
    public FieldSetMapper<MyProtoClass> fieldSetMapper(PropertyEditor emptyStringToNullPropertyEditor) {
        BeanWrapperFieldSetMapper<MyProtoClass> fieldSetMapper = new DelegatedFieldSetMapper();
        fieldSetMapper.setPrototypeBeanName("myProtoClass");
        Map<Class<String>, PropertyEditor> customEditors = new HashMap<Class<String>, PropertyEditor>();
        customEditors.put(String.class, emptyStringToNullPropertyEditor);
        fieldSetMapper.setCustomEditors(customEditors);
        return fieldSetMapper;

    }
DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(names.toArray(new String[names.size()]), values.toArray(new String[values.size()]));
要解决此问题,请更改为:

DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(values.toArray(new String[values.size()]), names.toArray(new String[names.size()]));

基于输入格式/数据的处理可以使用ItemProcessor的自定义实现来完成,该实现可以更改同一实体(由IteamReader填充)中的值,也可以创建一个新的输出实体。

您能否提供一些描述此方法的示例代码?我相信您的方法是,学员可以是
FieldSetMapper
s,例如
beanwrapperfiedsetmapper
。然后,在自定义(main)
FieldSetMapper
mapFieldSet
方法中,它将调用代理
FieldSetMapper
mapFieldSet
。您可以使用开关路由到右映射器,或者只使用
分类器
(PatternMatchingClassifier可能是正确的选择)。我不能给你一些代码,因为我没有空闲时间,但它很容易编写。虽然这解决了这个问题,但如果有人有更好/更简洁的方法来实现OP的既定目标,我会将此标记为答案。
DefaultFieldSet domainObjectFieldSet = new DefaultFieldSet(values.toArray(new String[values.size()]), names.toArray(new String[names.size()]));