使用Spring Integration和Batch读取CSV文件并将其插入数据库中

时间:2019-06-28 08:22:37

标签: spring-integration spring-batch

enter image description here enter image description here我有一个应用程序可以监视FTP文件夹中的特定csv文件foo.csv,一旦找到该文件,它会将其拉到我的本地文件并生成新的输出格式{ {1}},然后应用程序会将新文件bar.csv发送回FTP文件夹,并从本地删除它。

现在,我想介绍一个过程,该过程将读取bar.csv并将其插入数据库表中,然后再次将其发送给FTP服务器。

我假设可以使用Spring Batch Integration来完成此操作,但是我找不到执行该操作的方法。

以下是我的应用程序代码,以供参考和建议。

bar.csv

文件信息发送给Job类

public IntegrationFlow localToFtpFlow(Branch myBranch) {

    return IntegrationFlows.from(Files.inboundAdapter(new File(myBranch.getBranchCode()))
                    .filter(new ChainFileListFilter<File>()
                            .addFilter(new RegexPatternFileListFilter("final" + myBranch.getBranchCode() + ".csv"))
                            .addFilter(new FileSystemPersistentAcceptOnceFileListFilter(metadataStore(dataSource), "foo"))),//FileSystemPersistentAcceptOnceFileListFilter
            e -> e.poller(Pollers.fixedDelay(10_000)))
            .enrichHeaders(h ->h.headerExpression("file_originalFile", "new java.io.File('"+ myBranch.getBranchCode() +"/FEFOexport" + myBranch.getBranchCode() + ".csv')",true))
            .transform(p -> {
                LOG.info("Sending file " + p + " to FTP branch " + myBranch.getBranchCode());
                return p;
            })

            .log()
            .transform(m -> {
                        this.defaultSessionFactoryLocator.addSessionFactory(myBranch.getBranchCode(),createNewFtpSessionFactory(myBranch));
                        LOG.info("Adding factory to delegation");
                        return m;
            })

            .publishSubscribeChannel(s ->
                    s.subscribe(f -> f.transform(fileMessageToJobRequest()).handle(jobLaunchingGateway()).channel("nullChannel"))
                    .subscribe(h -> h.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), FileExistsMode.REPLACE)
                                     .useTemporaryFileName(true)
                                     .autoCreateDirectory(false)
                                     .remoteDirectory(myBranch.getFolderPath()), e -> e.advice(expressionAdvice()))))
            /*.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), FileExistsMode.REPLACE)
                    .useTemporaryFileName(true)
                    .autoCreateDirectory(false)
                    .remoteDirectory(myBranch.getFolderPath()), e -> e.advice(expressionAdvice()))*/
            .get();
}

@Bean
public FileMessageToJobRequest fileMessageToJobRequest(){
    FileMessageToJobRequest fileMessageToJobRequest = new FileMessageToJobRequest();
    fileMessageToJobRequest.setFileParameterName("input.file.name");
    fileMessageToJobRequest.setJob(orderJob);
    return fileMessageToJobRequest;
}

@Bean
public JobLaunchingGateway jobLaunchingGateway() {
    SimpleJobLauncher simpleJobLauncher = new SimpleJobLauncher();
    //simpleJobLauncher.setJobRepository(jobRepository);
    simpleJobLauncher.setTaskExecutor(new SyncTaskExecutor());
    JobLaunchingGateway jobLaunchingGateway = new JobLaunchingGateway(simpleJobLauncher);

    return jobLaunchingGateway;
}

在运行时添加流时出现错误。

public class FileMessageToJobRequest {

    private Job job;
    private String fileParameterName;

    public void setFileParameterName(String fileParameterName) {
        this.fileParameterName = fileParameterName;
    }

    public void setJob(Job job) {
        this.job = job;
    }

    @Transformer
    public JobLaunchRequest toRequest(Message<File> message) {
        JobParametersBuilder jobParametersBuilder =
                new JobParametersBuilder();

        jobParametersBuilder.addString(fileParameterName,
                message.getPayload().getAbsolutePath());//message.getPayload().getAbsolutePath()

        return new JobLaunchRequest(job, jobParametersBuilder.toJobParameters());
    }
}

1 个答案:

答案 0 :(得分:2)

您需要考虑在publishSubscribeChannel()定义中使用IntegtrationFlow。将您的.handle(Ftp.outboundAdapter())作为一个订户放置(最好是第二个)。作为第一个,它应该类似于.handle(jobLaunchingGateway).channel("nullChannel")

您可以在春季批注Reference Manual中阅读有关JobLaunchingGateway的信息。

重点是您要将同一条消息发送到多个地方。因此,PublishSubscribeChannel是最好的选择:https://docs.spring.io/spring-integration/docs/5.1.6.RELEASE/reference/html/#java-dsl-subflows

我建议您完全按顺序安排那些订户,因为您确实想在发送到FTP之前将其存储到DB中。如果没有executor,第二个订阅者将等到第一个订阅者完成其工作。

那里.channel("nullChannel")是必需的,因为JobLaunchingGateway实际上是网关,并且它返回JobExecution作为答复。由于您对此不感兴趣,因此只需忽略。当然,在该网关之后,您可能还有另一个handle(),以某种方式处理此JobExecution。关键是不要从第一手用户返回任何东西作为答复。它会以某种方式阻止您的主要工作。

更新

我认为.transform(fileMessageToJobRequest())必须在jobLaunchingGateway之前转到下面的第一个订阅者:

 .publishSubscribeChannel(s ->
                s.subscribe(f -> f.transform(fileMessageToJobRequest()).handle(jobLaunchingGateway()).channel("nullChannel"))
                .subscribe(h -> h.handle(Ftp.outboundAdapter(createNewFtpSessionFactory(myBranch), ...))

重点是您想将相同的文件发送到下一个handle(),但是在上游的转换器之后,它将被更改为JobLaunchRequest,这对{{1} },但不适用于jobLaunchingGateway

您的例外情况:

Ftp.outboundAdapter()

导致这段代码:Caused by: java.lang.NullPointerException at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:98) 。因此,以某种方式Assert.notNull(jobParameters, "The JobParameters must not be null.");会得出jobParametersBuilder.toJobParameters()

我无法提供太多自定义代码。

UPDATE2

好。看来您的问题出在null bean定义中。您在其中进行了显式的JobLaunchingGateway,而没有进行new SimpleJobLauncher()注入。

似乎在Spring Boot的JobRepository中有一个BasicBatchConfigurer,可以将其注入到此BatchConfigurerConfiguration中。因此,我们将通过NPE。此后还有其他错误,但这已经是另外一个故事了……