FlatFileItemWriter无法创建新文件或更新现有文件

时间:2018-06-20 12:55:57

标签: java mongodb csv spring-batch

[SpringBatch的新功能]我试图使用Spring Boot创建一个作业,该作业从MongoDB中读取名称,转换为小写字母,然后输出为CSV文件。我的读取器和处理器正在工作,而写入器却没有。

我的代码如下。

配置文件:

package bbye;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.item.data.MongoItemReader;
import org.springframework.batch.item.data.builder.MongoItemReaderBuilder;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor;
import org.springframework.batch.item.file.transform.DelimitedLineAggregator;
import org.springframework.batch.item.file.transform.FieldExtractor;
import org.springframework.batch.item.file.transform.LineAggregator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;

import hello.Person;

@Configuration
@EnableBatchProcessing
public class BatchConfigProcessing {


    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    @Autowired
    private MongoTemplate mongoTemplate;

    private String readQuery = "{}";

    // tag::readerwriterprocessor[]
    @Bean
    public MongoItemReader<Person> readMongo(DataSource dataSource) {
        return new MongoItemReaderBuilder<Person>()
                .name("mongoDocReader")
                .jsonQuery(readQuery)
                .targetType(Person.class)
                .sorts(sort())
                .template(mongoTemplate)
                .collection("people")
                .build();
    }

    @Bean
    public PersonDocProcessor processor() {
        return new PersonDocProcessor();
    }


    @Bean
    public FlatFileItemWriter<Person> writer() {
        /*FlatFileItemWriterBuilder<Person> writePerson =  new FlatFileItemWriterBuilder<Person>();
            writePerson.name("personDocWriter");
            writePerson.resource(new ClassPathResource("PersonExtracted.csv"));
            writePerson.lineAggregator(new DelimitedLineAggregator<Person>());
            writePerson.shouldDeleteIfExists(true);
            writePerson.build();*/

     FlatFileItemWriter<Person> fileWriter = new FlatFileItemWriter<>();
     fileWriter.setName("csvWriter");
     fileWriter.setResource(new ClassPathResource("PersonExtracted.csv"));
     fileWriter.setLineAggregator(lineAggregator());
     fileWriter.setForceSync(true);
     fileWriter.close();   
     return fileWriter; 
    }
    // end::readerwriterprocessor[]

    // tag::jobstep[]

    @Bean
    public Job exportUserJob(FileUploadNotificationListener listener, Step step1) {
        return jobBuilderFactory.get("exportUserJob")
            .incrementer(new RunIdIncrementer())
            .listener(listener)
            .flow(step1)
            .end()
            .build();
    }

    @Bean
    public Step step2(MongoItemReader<Person> reader) {
        return stepBuilderFactory.get("step2")
            .<Person, Person> chunk(10)
            .reader(reader)
            .processor(processor())
            .writer(writer())
            .build();
    }

    // end::jobstep[]

    public FieldExtractor<Person> fieldExtractor()
    {
        BeanWrapperFieldExtractor<Person> extractor = new BeanWrapperFieldExtractor<>();
        extractor.setNames( new String[] { "firstName",
                                            "lastName"});
        return extractor;

    }


    public LineAggregator<Person> lineAggregator() {
        DelimitedLineAggregator<Person> la = new DelimitedLineAggregator<Person>();
        la.setDelimiter(",");
        la.setFieldExtractor(fieldExtractor());
        return la;
    }

    public Map<String, Sort.Direction> sort(){
        String firstName = "firstName";
        Map<String, Sort.Direction> sortMap = new HashMap();
        sortMap.put(firstName, Sort.DEFAULT_DIRECTION);
        return sortMap;
    }


}

处理器文件

package bbye;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.stereotype.Component;

import hello.Person;

@Component
public class PersonDocProcessor implements ItemProcessor<Person, Person> {

    private static final Logger log = LoggerFactory.getLogger(PersonDocProcessor.class);

    @Override
    public Person process(final Person person) throws Exception {
        final String firstName = person.getFirstName().toLowerCase();
        final String lastName = person.getLastName().toLowerCase();

        final Person transformedPerson = new Person(firstName, lastName);

        log.info("Converting (" + person + ") into (" + transformedPerson + ")");

        return transformedPerson;
    }
}

Listener

package bbye;

import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.stereotype.Component;

@Component
public class FileUploadNotificationListener implements JobExecutionListener {

    @Override
    public void beforeJob(JobExecution jobExecution) {
        System.out.println("===== listening for job - mongoReader - fileWriter ====");

    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        System.out.println("==== file write job completed =====");

    }
}

此人是一个简单的POJO。带有和不带有手动文件创建的堆栈跟踪如下:

  1. 如果src / main / resources下不存在该文件,则FlatFileItemWriter不会创建新文件并引发
org.springframework.batch.item.ItemStreamException: Could not convert resource to file: [class path resource [PersonExtracted.csv]]
    at org.springframework.batch.item.file.FlatFileItemWriter.getOutputState(FlatFileItemWriter.java:399) ~[spring-batch-infrastructure-4.0.1.RELEASE.jar:4.0.1.RELEASE]
    at org.springframework.batch.item.file.FlatFileItemWriter.open(FlatFileItemWriter.java:337) ~[spring-batch-infrastructure-4.0.1.RELEASE.jar:4.0.1.RELEASE]
........
Caused by: java.io.FileNotFoundException: class path resource [PersonExtracted.csv] cannot be resolved to URL because it does not exist
  1. 如果我手动创建PersonExtracted.csv文件,该程序将运行且没有错误,但不会向该文件写入任何内容。实际上,将返回一个空白文件。堆栈跟踪如下。
:: Spring Boot ::        (v2.0.2.RELEASE)

2018-06-19 11:35:17.663  INFO 25136 --- [           main] hello.Application                        : Starting Application on MyPC with PID 25136 (C:\eclipse-workspace\gs-batch-processing-master\complete\target\classes started by shristi in C:\eclipse-workspace\gs-batch-processing-master\complete)
2018-06-19 11:35:17.666  INFO 25136 --- [           main] hello.Application                        : No active profile set, falling back to default profiles: default
2018-06-19 11:35:17.689  INFO 25136 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@15bb6bea: startup date [Tue Jun 19 11:35:17 EDT 2018]; root of context hierarchy
2018-06-19 11:35:18.135  INFO 25136 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2018-06-19 11:35:18.136  WARN 25136 --- [           main] com.zaxxer.hikari.util.DriverDataSource  : Registered driver with driverClassName=org.hsqldb.jdbcDriver was not found, trying direct instantiation.
2018-06-19 11:35:18.282  INFO 25136 --- [           main] com.zaxxer.hikari.pool.PoolBase          : HikariPool-1 - Driver does not support get/set network timeout for connections. (feature not supported)
2018-06-19 11:35:18.284  INFO 25136 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2018-06-19 11:35:18.293  INFO 25136 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executing SQL script from URL [file:/C:/eclipse-workspace/gs-batch-processing-master/complete/target/classes/schema-all.sql]
2018-06-19 11:35:18.297  INFO 25136 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executed SQL script from URL [file:/C:/eclipse-workspace/gs-batch-processing-master/complete/target/classes/schema-all.sql] in 4 ms.
2018-06-19 11:35:18.518  INFO 25136 --- [           main] org.mongodb.driver.cluster               : Cluster created with settings {hosts=[localhost:27017], mode=SINGLE, requiredClusterType=UNKNOWN, serverSelectionTimeout='30000 ms', maxWaitQueueSize=500}
2018-06-19 11:35:18.552  INFO 25136 --- [localhost:27017] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:1, serverValue:140}] to localhost:27017
2018-06-19 11:35:18.554  INFO 25136 --- [localhost:27017] org.mongodb.driver.cluster               : Monitor thread successfully connected to server with description ServerDescription{address=localhost:27017, type=STANDALONE, state=CONNECTED, ok=true, version=ServerVersion{versionList=[3, 6, 0]}, minWireVersion=0, maxWireVersion=6, maxDocumentSize=16777216, logicalSessionTimeoutMinutes=30, roundTripTimeNanos=1438717}
2018-06-19 11:35:18.723  INFO 25136 --- [           main] o.s.b.c.r.s.JobRepositoryFactoryBean     : No database type set, using meta data indicating: HSQL
2018-06-19 11:35:18.770  INFO 25136 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : No TaskExecutor has been set, defaulting to synchronous executor.
2018-06-19 11:35:18.778  INFO 25136 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executing SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql]
2018-06-19 11:35:18.781  INFO 25136 --- [           main] o.s.jdbc.datasource.init.ScriptUtils     : Executed SQL script from class path resource [org/springframework/batch/core/schema-hsqldb.sql] in 3 ms.
2018-06-19 11:35:18.870  INFO 25136 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-06-19 11:35:18.871  INFO 25136 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Bean with name 'dataSource' has been autodetected for JMX exposure
2018-06-19 11:35:18.873  INFO 25136 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Located MBean 'dataSource': registering with JMX server as MBean [com.zaxxer.hikari:name=dataSource,type=HikariDataSource]
2018-06-19 11:35:18.880  INFO 25136 --- [           main] hello.Application                        : Started Application in 1.357 seconds (JVM running for 2.284)
2018-06-19 11:35:18.881  INFO 25136 --- [           main] o.s.b.a.b.JobLauncherCommandLineRunner   : Running default command line with: []
2018-06-19 11:35:18.908  INFO 25136 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=exportUserJob]] launched with the following parameters: [{run.id=1}]
===== listening for job - mongoReader - fileWriter ====
2018-06-19 11:35:18.917  INFO 25136 --- [           main] o.s.batch.core.job.SimpleStepHandler     : Executing step: [step2]
2018-06-19 11:35:18.995  INFO 25136 --- [           main] org.mongodb.driver.connection            : Opened connection [connectionId{localValue:2, serverValue:141}] to localhost:27017
2018-06-19 11:35:19.022  INFO 25136 --- [           main] bbye.PersonDocProcessor                  : Converting (firstName: ALICE, lastName: WONDERLAND) into (firstName: alice, lastName: wonderland)
2018-06-19 11:35:19.022  INFO 25136 --- [           main] bbye.PersonDocProcessor                  : Converting (firstName: FIRSTNAME, lastName: LASTNAME) into (firstName: firstname, lastName: lastname)
2018-06-19 11:35:19.022  INFO 25136 --- [           main] bbye.PersonDocProcessor                  : Converting (firstName: JANE, lastName: DOE) into (firstName: jane, lastName: doe)
2018-06-19 11:35:19.022  INFO 25136 --- [           main] bbye.PersonDocProcessor                  : Converting (firstName: JOHN, lastName: DOE) into (firstName: john, lastName: doe)
2018-06-19 11:35:19.022  INFO 25136 --- [           main] bbye.PersonDocProcessor                  : Converting (firstName: MARK, lastName: WINN) into (firstName: mark, lastName: winn)
==== file write job completed =====
2018-06-19 11:35:19.031  INFO 25136 --- [           main] o.s.b.c.l.support.SimpleJobLauncher      : Job: [FlowJob: [name=exportUserJob]] completed with the following parameters: [{run.id=1}] and the following status: [COMPLETED]

2018-06-19 11:35:19.032  INFO 25136 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@15bb6bea: startup date [Tue Jun 19 11:35:17 EDT 2018]; root of context hierarchy
2018-06-19 11:35:19.033  INFO 25136 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2018-06-19 11:35:19.034  INFO 25136 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans
2018-06-19 11:35:19.035  INFO 25136 --- [       Thread-2] org.mongodb.driver.connection            : Closed connection [connectionId{localValue:2, serverValue:141}] to localhost:27017 because the pool has been closed.
2018-06-19 11:35:19.036  INFO 25136 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2018-06-19 11:35:19.037  INFO 25136 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.

2 个答案:

答案 0 :(得分:1)

我认为我们应该使用FileSystemResource而不是ClassPathResource。您可以尝试告诉我们吗?

答案 1 :(得分:0)

回答问题。事实证明,编写者的工作正常,但我查看的是错误的文件。 使用 ClassPathResource 时,将在 target / classes 目录下创建和更新文件。但是,我正在查看 src / main / resources 目录下的PersonExtracted.csv,该目录从未更新。

如果我指定 FileSystemResource ,则会在指定位置创建并更新文件。