You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch Java读取多片段XML文件入库仅插入最后一条数据问题咨询

问题根因

你遇到的仅最后一条数据插入成功的问题,核心原因如下:

  1. 参数被循环覆盖:ItemPreparedStatementSetter的设计逻辑是,每个传入的Transaction对象对应1行待插入记录,你在setValues方法中循环遍历同一个Transaction下的多个Item,反复给同一个PreparedStatement的第12~29位参数赋值,每次循环都会覆盖上一次的参数值,最终只有最后一个Item的属性会被保留到插入语句中。
  2. 参数赋值不全:你的插入SQL一共定义了34个占位符,当前代码仅完成了前29位参数的赋值,剩余的Ticket_Number、Trans_Time_HHMMSS等字段完全没有设值,会导致插入数据不符合预期。
  3. 字符串判空不规范:代码中用!= ""判断字符串是否为空,Java中字符串是引用类型,==/!=比较的是内存地址而非值,既可能出现判断逻辑错误,还可能触发空指针异常。
  4. 额外检查点:确认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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 08:45:03