如何在 Spring Batch 中分别读取平面文件头和正文

Posted

技术标签:

【中文标题】如何在 Spring Batch 中分别读取平面文件头和正文【英文标题】:How to read flat file header and body separately in Spring Batch 【发布时间】:2018-08-06 10:44:45 【问题描述】:

我正在使用 Spring Batch 和 Spring Boot 做一个简单的批处理作业。

我需要读取一个平面文件,将标题数据(第一行)与正文数据(其余行)分开以进行单独的业务逻辑处理,然后将所有内容写入一个文件。

如您所见,header 有 5 个参数必须映射到一个类,而 body 有 12 个参数必须映射到不同的类。

我首先想到的是使用 FlatFileItemReader 并跳过标题。然后使用 skippedLinesCallback 来处理该行,但我不知道该怎么做。

我是 Spring Batch 和 Java Config 的新手。如果有人可以帮助我为我的问题写一个解决方案,我真的很感激!

我把输入文件留在这里:

01.01.2017|SUBDCOBR|12:21:23|01/12/2016|31/12/2016
01.01.2017|12345678231234|0002342434|BORGIA RUBEN|27-32548987-9|FA|A|2062-
00010443/444/445|142,12|30/08/2017|142,01
01.01.2017|12345673201234|2342434|ALVAREZ ESTHER|27-32533987-9|FA|A|2062-
00010443/444/445|142,12|30/08/2017|142,02
01.01.2017|12345673201234|0002342434|LOPEZ LUCRECIA|27-32553387-9|FA|A|2062-
00010443/444/445|142,12|30/08/2017|142,12
01.01.2017|12345672301234|0002342434|SILVA JESUS|27-32558657-9|NC|A|2062-
00010443|142,12|30/08/2017|142,12

干杯!

编辑 1: 这将是我的第一次尝试。我的“body” POJO 称为 DetalleFacturacion,而我的“header” POJO 是 CabeceraFacturacion。读者我想用 DetalleFacturacion pojo 来做,所以我可以跳过标题并稍后处理它......但是我不确定如何将标题的数据分配给 CabeceraFacturacion。

public FlatFileItemReader<DetalleFacturacion> readerDetalleFacturacion()

    FlatFileItemReader<DetalleFacturacion> reader = new FlatFileItemReader<>();

    reader.setLinesToSkip(1);
    reader.setResource(new ClassPathResource("/inputFiles/GLEO-MN170100-PROCESO01-SUBDFACT-000001.txt"));

    DefaultLineMapper<DetalleFacturacion> detalleLineMapper = new DefaultLineMapper<>();

    DelimitedLineTokenizer tokenizerDet = new DelimitedLineTokenizer("|");
    tokenizerDet.setNames(new String[] "fechaEmision", "tipoDocumento", "letra", "nroComprobante",  
                                "nroCliente", "razonSocial", "cuit", "montoNetoGP", "montoNetoG3",
                                "montoExento", "impuestos", "montoTotal");

    LineCallbackHandler skippedLineCallback = new LineCallbackHandler() 

        @Override
        public void handleLine(String line) 

            String[] headerSeparado = line.split("|");

            String printDate = headerSeparado[0];
            String reportIdentifier = headerSeparado[1];
            String tituloReporte = headerSeparado[2];
            String fechaDesde = headerSeparado[3];
            String fechaHasta = headerSeparado[4];

            CabeceraFacturacion cabeceraFacturacion = new CabeceraFacturacion();
            cabeceraFacturacion.setPrintDate(printDate);
            cabeceraFacturacion.setReportIdentifier(reportIdentifier);
            cabeceraFacturacion.setTituloReporte(tituloReporte);
            cabeceraFacturacion.setFechaDesde(fechaDesde);
            cabeceraFacturacion.setFechaHasta(fechaHasta);

        
    ;

    reader.setSkippedLinesCallback(skippedLineCallback);

    detalleLineMapper.setLineTokenizer(tokenizerDet);
    detalleLineMapper.setFieldSetMapper(new DetalleFieldSetMapper());
    detalleLineMapper.afterPropertiesSet();
    reader.setLineMapper(detalleLineMapper);

    // Test to check if it is saving correctly data in CabeceraFacturacion
    CabeceraFacturacion cabeceraFacturacion = new CabeceraFacturacion();
    System.out.println("Print Date:"+cabeceraFacturacion.getPrintDate());
    System.out.println("Report Identif: 
    "+cabeceraFacturacion.getReportIdentifier());

    return reader;

【问题讨论】:

【参考方案1】:

你是对的。您需要使用skippedLinesCallback 来处理跳行。

您需要实现LineCallbackHandler 接口并在handleLine 方法中添加您的处理。

LineCallbackHandler 接口传递文件中要跳过的行的原始行内容。如果linesToSkip设置为2,那么这个接口会被调用两次。

这就是您可以定义 Reader 的方式。

Java 配置 - Spring Batch 4

@Bean
    public FlatFileItemReader<POJO> myReader() 

        return FlatFileItemReader<pojo>().
                .setResource(new FileSystemResource("resources/players.csv"));
                .name("myReader")
                .delimited()
                .delimiter(",")
                .names("pro1,pro2,pro3")
                .targetType(POJO.class)
                .skippedLinesCallback(skippedLinesCallback)             
                .build();

    

【讨论】:

非常感谢 Niraj 的回答。这是我尝试过的,但我仍然不知道如何将标题的数据存储在一个 pojo 中,将正文存储在另一个 pojo 中。因为当我定义 FlatFileItemReader 时,我只在 . 中使用了一个 pojo 如果您将linesToSkip 设置为1,那么该行(标题行)的LineCallbackHandler.handleLine 方法将被调用。 Batch Framework 会将原始行内容传递给该方法。那么您需要根据需要解析该行。 完美。我理解到这一点。我再次更新了代码。我用线映射器和标记器阅读了这条线。这是阅读它的方式吗?因为在测试中我确实查看了它是否正确保存了数据,所以两个参数都为 null。 我明白了。它只是一个原始的字符串线。然后我可以使用 line.split("|") 并使用 set 保存参数。非常感谢尼拉吉! 不客气……你能接受答案吗?这也将帮助其他可能面临同样问题的人。谢谢

以上是关于如何在 Spring Batch 中分别读取平面文件头和正文的主要内容,如果未能解决你的问题,请参考以下文章

使用 Spring Batch 实现 ETL

在 Spring Batch 步骤中移动 HashMap

如何在 Spring Batch 中使用 Spring 事务支持

Spring Batch中如何读取多个CSV文件合并数据进行处理?

Spring Batch 并行读取数据库

Spring-batch学习总结—ItemReader普通文件,数据库,XML,多文件数据读取