如何使用BeanWrapperFieldSetMapper来映射字段子集?

问题描述 投票:0回答:4

我有一个 Spring 批处理应用程序,其中

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

我知道我可以使用自定义

FieldSetMapper
来执行此操作,但随后我必须对所有其他字段(其中有相当多)的映射进行编码。或者,我可以通过
ItemProcessor
来阅读这篇文章,但是我的域(原型)对象必须有一个代表指标字段的属性(我不喜欢这样做,因为它实际上并不是业务域的一部分)。

是否可以使用自定义

FieldSetMapper
仅映射这些自定义字段并将其他映射委托给
BeanWrapperFieldSetMapper
?或者有其他更好的方法来解决这个问题吗?

这是我当前尝试使用自定义

FieldSetMapper
并委托给
BeanWrapperFieldSetMapper
:

    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);
        }
        
    }

但是,抛出了

FlatFileParseException
。批量配置类的相关部分如下:

    @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;
    
        }

最后,CSV 平面文件如下所示:

value,bar,foo,indicator
abc,,,y
xyz,,,n
spring-batch
4个回答
3
投票

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

   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;
        }

    });

2
投票

除了导致 FlatFileParseException 的一个问题之外,代码实际上完成了所需的任务。

DelegatedFieldSetMapper
包含的问题如下:

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()]));

1
投票

编写您自己的

FieldSetMapper
,其中包含一组准备好的代表。
这些代表是为每种不同类型的字段映射预先构建的。
在您的对象路由中,根据指示符字段纠正委托(例如,使用
Classifier
)。
我看不到任何其他方法,但这个解决方案非常容易维护。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.