我有一个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
答案 0 :(得分: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 :(得分:1)
用自己的一组准备好的代表写自己的FieldSetMapper
这些代表是为每种不同类型的字段映射而预先构建的
在您的对象路径中,根据指标字段更正委托(例如,使用Classifier
)
我看不到任何其他方式,但这个解决方案很容易维护。
答案 2 :(得分:1)
让我们说BatchWorkObject是要映射的类。 这是Spring Boot样式的示例代码,只需要添加自定义逻辑。
currentState
答案 3 :(得分:0)
可以使用ItemProcessor的自定义实现来完成基于输入格式/数据的处理,该实现要么更改同一实体(由IteamReader填充)中的值,要么创建一个新的输出实体。