Spring Batch任务重复读取同一Mongo文档问题排查与解决
问题原因分析
你的自定义阅读器会重复读取同一文档,核心原因有两个:
- 无读取状态跟踪:每次调用
read()方法时,都会执行完全相同的findOne("name=Sama")查询,没有任何机制标记该文档已被读取,所以每次都返回同一个符合条件的结果。 - 未触发终止条件:Spring Batch的ItemReader会持续调用
read()方法,直到方法返回null才会停止读取。你的代码永远返回同一个非null的文档,导致任务无限循环。
解决方案
推荐两种方式解决这个问题:
方式一:使用Spring Batch官方的MongoItemReader(优先推荐)
Spring Batch提供了开箱即用的MongoItemReader,它内置了游标分页和读取位置跟踪逻辑,无需自己实现复杂的状态管理。示例代码如下:
@Bean public MongoItemReader<Employee> mongoItemReader(MongoTemplate mongoTemplate) { return new MongoItemReaderBuilder<Employee>() .name("employeeItemReader") .template(mongoTemplate) .collection("sourceCollectionName") .query(new Query(Criteria.where("name").is("Sama"))) .targetType(Employee.class) .build(); }
这个阅读器会自动遍历符合查询条件的文档,不会重复读取,遍历完成后返回null终止任务。
方式二:修改自定义阅读器,添加读取状态跟踪
如果必须自定义阅读器,需要实现已读取文档的标记逻辑,比如给文档增加processed字段:
@Override public Employee read(){ // 只查询未处理的name=Sama的文档 Query query = new Query(Criteria.where("name").is("Sama").and("processed").is(false)); // 原子更新+查询,避免并发问题 Update update = new Update().set("processed", true); FindAndModifyOptions options = new FindAndModifyOptions().returnNew(false); return mongoTemplate.findAndModify(query, update, options, Employee.class); }
当所有符合条件的文档都被标记为已处理后,findAndModify会返回null,触发任务终止。
另外,也可以通过记录已读取文档的ID列表来排除已处理文档,但这种方式在数据量较大时内存占用较高,不如状态字段的方式高效。
内容的提问来源于stack exchange,提问作者aksh_18
相关产品推荐
相关产品推荐

