Spring Batch多表写入:ItemWriter选型及Classifier代码问题排查
问题解答
1. ClassifierCompositeItemWriter vs CompositeItemWriter 选型
- ClassifierCompositeItemWriter更适用:
- CompositeItemWriter会串行执行所有配置的ItemWriter,把同一个输入对象传给所有Writer,完全不符合你拆分VO中不同实体分别写入对应表的需求。
- ClassifierCompositeItemWriter通过分类器(Classifier)将不同类型的数据路由到对应的ItemWriter,能精准把单个Company、每个DeptEntity、每个EmployeeEntity分发到对应表的Writer,完美匹配你的场景。
2. 解决clazz instanceof List编译错误及类型判断问题
泛型擦除导致运行时无法直接识别List<DeptEntity>这类带泛型的类型,你需要调整判断逻辑:
正确的类型判断实现
先判断对象是否为List实例,再通过列表元素(非空时)的类型匹配对应Writer,示例代码如下:
public class ClientClassifier implements Classifier<Object, ItemWriter<?>> { private ItemWriter<CompanyEntity> companyWriter; private ItemWriter<DeptEntity> deptWriter; private ItemWriter<EmployeeEntity> employeeWriter; @Override public ItemWriter<?> classify(Object object) { if (object instanceof CompanyEntity) { return companyWriter; } else if (object instanceof List) { List<?> list = (List<?>) object; if (!list.isEmpty()) { Object firstItem = list.get(0); if (firstItem instanceof DeptEntity) { return deptWriter; } else if (firstItem instanceof EmployeeEntity) { return employeeWriter; } } throw new IllegalArgumentException("Empty list or unknown element type"); } throw new IllegalArgumentException("Unknown object type: " + object.getClass()); } // 省略setter方法 }
注意:如果你的处理器输出的是完整的
ClientCompositeVO对象,而非拆分后的单个实体/列表,需要先在Classifier里拆分VO,或者调整处理器逻辑:让处理器把VO拆分为多个独立item(比如一个Company + 所有Dept + 所有Employee),再由Classifier按类型路由。
3. 更简洁的解决方案:自定义ItemWriter直接拆分
如果觉得Classifier配置繁琐,直接写自定义ItemWriter<ClientCompositeVO>,手动完成拆分和多表写入,逻辑更直观:
public class ClientCompositeWriter implements ItemWriter<ClientCompositeVO> { private ItemWriter<CompanyEntity> companyWriter; private ItemWriter<DeptEntity> deptWriter; private ItemWriter<EmployeeEntity> employeeWriter; @Override public void write(List<? extends ClientCompositeVO> items) throws Exception { for (ClientCompositeVO vo : items) { // 写入Company表 companyWriter.write(Collections.singletonList(vo.getCompany())); // 写入Department表 if (vo.getDepts() != null && !vo.getDepts().isEmpty()) { deptWriter.write(vo.getDepts()); } // 写入Employee表 if (vo.getEmployees() != null && !vo.getEmployees().isEmpty()) { employeeWriter.write(vo.getEmployees()); } } } // 省略setter方法 }
这种方式完全满足你"仅读取一次源数据,在一个ItemWriter中完成拆分"的要求,无需处理Classifier的类型判断问题。
配置示例(Spring Boot环境)
无论用哪种方案,都需要先配置单表的JdbcBatchItemWriter,再注入到目标Writer中:
@Bean public JdbcBatchItemWriter<CompanyEntity> companyWriter(DataSource dataSource) { return new JdbcBatchItemWriterBuilder<CompanyEntity>() .dataSource(dataSource) .sql("INSERT INTO company(id, name) VALUES (:id, :name)") .beanMapped() .build(); } // 同理配置deptWriter和employeeWriter // 自定义Writer配置 @Bean public ItemWriter<ClientCompositeVO> compositeWriter(ItemWriter<CompanyEntity> companyWriter, ItemWriter<DeptEntity> deptWriter, ItemWriter<EmployeeEntity> employeeWriter) { ClientCompositeWriter writer = new ClientCompositeWriter(); writer.setCompanyWriter(companyWriter); writer.setDeptWriter(deptWriter); writer.setEmployeeWriter(employeeWriter); return writer; }
内容的提问来源于stack exchange,提问作者AbNig
相关产品推荐
相关产品推荐

