Java Spring批处理以聚合值并写入单个值
我正在使用SpringBatch,我需要实现以下目标Java Spring批处理以聚合值并写入单个值,java,spring,spring-batch,Java,Spring,Spring Batch,我正在使用SpringBatch,我需要实现以下目标 读取包含日期和金额等详细信息的csv文件 合计同一日期所有金额的总和 保留一个带有日期和总和的条目 我过去使用过批处理,我想到了以下方法。创建一个包含2个步骤的批处理 第1步: Reader:使用FlatFileItemReader遍历整个文件 处理器:使用键作为日期和值作为金额填充地图。如果存在条目,则获取该值并将其添加到新值中 作者:没有操作作者,因为我不想写 第二步: 读卡器:循环浏览地图的值 作者:坚持这些价值观 我能够完成步骤1,在
地图。已使用@JobScope
我被困在如何创建步骤2的读取器上,而步骤2只需要读取值列表。我尝试了ListItemReader
,但无法从ListItemReader
访问Map
请提供解决方案,或者您是否有更好的方法来解决此问题
谢谢选项1:
如果您的CV已经按日期排序,则可以实现一个组读取器,该读取器读取行,直到键值更改为止。之后,整个组可以作为一个项目传递给处理器
这样的组读取器可能如下所示:
private SingleItemPeekableItemReader<I> reader;
private ItemReader<I> peekReaderDelegate;
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(peekReaderDelegate, "The 'itemReader' may not be null");
this.reader= new SingleItemPeekableItemReader<I>();
this.reader.setDelegate(peekReaderDelegate);
}
@Override
// GroupDTO is just a simple container. It is also possible to use
// List<I> instead of GroupDTO<I>
public GroupDTO<I> read() throws Exception {
State state = State.NEW; // a simple enum with the states NEW, READING, and COMPLETE
GroupDTO<I> group = null;
I item = null;
while (state != State.COMPLETE) {
item = reader.read();
switch (state) {
case NEW: {
if (item == null) {
// end reached
state = State.COMPLETE;
break;
}
group = new GroupDTO<I>();
group.addItem(item);
state = State.READING;
I nextItem = reader.peek();
// isGroupBreak returns true, if 'item' and 'nextItem' do NOT belong to the same group
if (nextItem == null || getGroupBreakStrategy.isGroupBreak(item, nextItem)) {
state = State.COMPLETE;
}
break;
}
case READING: {
group.addItem(item);
// peek and check if there the peeked entry has a new date
I nextItem = peekEntry();
// isGroupBreak returns true, if 'item' and 'nextItem' do NOT belong to the same group
if (nextItem == null || getGroupBreakStrategy.isGroupBreak(item, nextItem)) {
state = State.COMPLETE;
}
break;
}
default: {
throw new org.springframework.expression.ParseException(groupCounter, "ParsingError: Reader is in an invalid state");
}
}
}
return group;
}
私有SingleItemPeakableItemReader;
私有项目阅读器peekReaderDelegate;
@凌驾
public void afterPropertieSet()引发异常{
Assert.notNull(peekReaderDelegate,“itemReader”不能为null);
this.reader=新的SingleItemPeakableItemReader();
this.reader.setDelegate(peekReaderDelegate);
}
@凌驾
//GroupDTO只是一个简单的容器。也可以使用
//列表而不是GroupDTO
public GroupDTO read()引发异常{
State State=State.NEW;//状态为NEW、READING和COMPLETE的简单枚举
GroupDTO group=null;
I项=空;
while(state!=state.COMPLETE){
item=reader.read();
开关(状态){
新案例:{
如果(项==null){
//到达终点
state=state.COMPLETE;
打破
}
group=新的GroupDTO();
组.附加项(项目);
state=state.READING;
I nextItem=reader.peek();
//如果“item”和“nextItem”不属于同一组,isGroupBreak将返回true
if(nextItem==null | | getGroupBreakStrategy.isGroupBreak(项,nextItem)){
state=state.COMPLETE;
}
打破
}
案例解读:{
组.附加项(项目);
//查看并检查查看的条目是否有新日期
I nextItem=peek条目();
//如果“item”和“nextItem”不属于同一组,isGroupBreak将返回true
if(nextItem==null | | getGroupBreakStrategy.isGroupBreak(项,nextItem)){
state=state.COMPLETE;
}
打破
}
默认值:{
抛出新的org.springframework.expression.ParseException(groupCounter,“ParsingError:Reader处于无效状态”);
}
}
}
返回组;
}
您需要一个SingleItemPeakableItemReader,以便预读下一个元素。这一个包裹了你的普通读者
备选案文2:
第一步正如您所建议的,但只需为第二步编写一个tasklet即可。不需要使用reader-process-writer方法,而是可以使用一个简单的tasklet将地图的内容写入文件
备选案文3:
如果你真的想在第二步中使用读写器-处理器-编写器的方法,那就编写你自己的读写器,在你的地图上迭代
类似(我没有测试该代码):
公共类MapReader实现ItemReader{
私有映射容器;
private Iterator请发布代码的相关部分…您是如何声明映射的,步骤2的读取器是什么样子的?您可以尝试以下方法(2个选项StepContext或使用Java Bean)
public class MapReader implements ItemReader {
private MapContainer container;
private Iterator<Map.Entry<Date, Integer> mapIterator;
@PostConstruct
public void afterPropertiesSet() {
Assert.notNull(container);
iterator = container.getMap().entry().iterator;
}
public void setMapContainer(MapContainer container) {
this.container = container;
}
public Map.Entry<Date, Integer> read() {
if (iterator.hasNext()) {
return iterator.next();
}
return null;
}
}
@Component
public class MapContainer {
private Map<Date, Integer> data = new Hashmap<>();
public Map<Date, Integer> getMap() {
return data;
}
// add modifier method as needed for step 1
}