Spring Batch Java读取多片段XML文件入库仅插入最后一条数据问题咨询
问题根因
你遇到的仅最后一条数据插入成功的问题,核心原因如下:
- 参数被循环覆盖:
ItemPreparedStatementSetter的设计逻辑是,每个传入的Transaction对象对应1行待插入记录,你在setValues方法中循环遍历同一个Transaction下的多个Item,反复给同一个PreparedStatement的第12~29位参数赋值,每次循环都会覆盖上一次的参数值,最终只有最后一个Item的属性会被保留到插入语句中。 - 参数赋值不全:你的插入SQL一共定义了34个占位符,当前代码仅完成了前29位参数的赋值,剩余的
Ticket_Number、Trans_Time_HHMMSS等字段完全没有设值,会导致插入数据不符合预期。 - 字符串判空不规范:代码中用
!= ""判断字符串是否为空,Java中字符串是引用类型,==/!=比较的是内存地址而非值,既可能出现判断逻辑错误,还可能触发空指针异常。 - 额外检查点:确认
Transaction类的inventoryItem属性是否加了@XmlElement(name = "Item")注解,保证XML中的<Item>节点能正确映射到Java属性。
解决步骤
建议采用一对多展开的方案修正,逻辑更清晰也符合Spring Batch的设计规范:
步骤1:新增中间映射实体
新增和数据库表xml_sales字段一一对应的实体类XmlSalesRecord,同时包含Transaction的公共字段和单个Item的属性:
@Data public class XmlSalesRecord { // 对应Transaction的公共字段 private String S_18_Description_Gas_Coupon; private String S_18_Description_Manufacturers; // ... 其他Transaction公共字段,包括Ticket_Number、Trans_Time_HHMMSS等 // 对应单个Item的字段 private String Item_Number; private String English_Description; // ... 其他Item字段 }
步骤2:新增扁平化处理器
用FlatMapItemProcessor把单个Transaction展开为多个XmlSalesRecord对象,每个Item对应一个XmlSalesRecord:
@Bean public FlatMapItemProcessor<Transaction, XmlSalesRecord> flattenProcessor() { return transaction -> { List<XmlSalesRecord> records = new ArrayList<>(); if (CollectionUtils.isEmpty(transaction.getInventoryItem())) { return records; } // 遍历Transaction下的所有Item,逐个构造XmlSalesRecord for (Item item : transaction.getInventoryItem()) { if (ObjectUtils.isEmpty(item.getItem_Number())) { continue; } XmlSalesRecord record = new XmlSalesRecord(); // 复制Transaction公共字段 record.setS_18_Description_Gas_Coupon(transaction.getS_18_Description_Gas_Coupon()); record.setTicket_Number(transaction.getTicket_Number()); record.setTrans_Time_HHMMSS(transaction.getTrans_Time_HHMMSS()); // ... 其他公共字段赋值 // 复制Item字段 record.setItem_Number(item.getItem_Number()); record.setEnglish_Description(item.getEnglish_Description()); // ... 其他Item字段赋值 records.add(record); } return records; }; }
步骤3:修正Writer和参数赋值逻辑
调整JdbcBatchItemWriter的泛型为XmlSalesRecord,重写ItemPreparedStatementSetter,直接给单个XmlSalesRecord赋值即可,不需要循环:
@Bean public JdbcBatchItemWriter<XmlSalesRecord> writer() { JdbcBatchItemWriter<XmlSalesRecord> writer = new JdbcBatchItemWriter<>(); writer.setDataSource(dataSource); // 原有SQL不用改 writer.setSql("insert into xml_sales(...) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); writer.setItemPreparedStatementSetter((record, ps) -> { // 按顺序赋值所有34个参数 ps.setString(1, record.getS_18_Description_Gas_Coupon()); ps.setString(2, record.getS_18_Description_Manufacturers()); // ... 赋值到第29位 ps.setString(30, record.getTicket_Number()); ps.setString(31, record.getTrans_Time_HHMMSS()); ps.setString(32, record.getTrans_Date()); ps.setString(33, record.getAmended_TRN_Date()); ps.setString(34, record.getAmended_TRN_Time()); }); return writer; }
步骤4:调整Step配置
把扁平化处理器加入到Step的处理链路中:
@Bean public Step xmlToDbStep(StaxEventItemReader<Transaction> reader, FlatMapItemProcessor<Transaction, XmlSalesRecord> processor, JdbcBatchItemWriter<XmlSalesRecord> writer) { return stepBuilderFactory.get("xmlToDbStep") .<Transaction, XmlSalesRecord>chunk(10) .reader(reader) .processor(processor) .writer(writer) .build(); }
内容的提问来源于stack exchange,提问作者Shayan Naveed
相关产品推荐
相关产品推荐

