【问题标题】:Spring batch to upload a CSV file and insert into database accordinglySpring批处理上传CSV文件并相应地插入数据库
【发布时间】:2018-05-06 16:14:04
【问题描述】:

我的项目有这个要求,用户上传一个 CSV 文件,该文件必须被推送到 mysql 数据库。我知道我们可以使用 Spring 批处理来处理大量记录。但是我找不到任何教程/示例代码来满足我的这个要求。我遇到的所有教程都只是硬编码了 CSV 文件名,如下所示:

https://spring.io/guides/gs/batch-processing/

我需要使用用户上传的文件并进行相应的处理。任何帮助将不胜感激..

如果不使用Spring batch,有没有其他方法可以将上传的CSV数据插入mysql?

【问题讨论】:

  • 当你想运行一些工作,不断地从某个地方获取输入,处理它并将它保存在某个地方时,Spring 批处理很有用。这样做的主要优点是 spring 将在单独的线程中执行这三个步骤,并且可以在块中执行此操作。这节省了时间和空间的复杂性。在您的情况下,它必须在用户操作时触发,所以我认为在这种情况下弹簧批处理并不理想。可以使用它,前提是您首先从用户那里获取 csv 文件并将其存储在内存或某处,然后配置 Spring Batch 以将其作为输入读取
  • @pvpkiran,你有实现这个的示例代码吗?

标签: java spring spring-batch


【解决方案1】:

请将此作为主要参考:http://walkingtechie.blogspot.co.uk/2017/03/spring-batch-csv-file-to-mysql.html 这说明了如何使用 Batch 将 CSV 文件导入 MySQL 数据库。

但是,正如您所说,所有示例都假定硬编码文件不是您想要的。

在下面的代码中,重要的位(与我提供的链接中的示例不同)是控制器,它采用多部分文件并将其保存在临时文件夹中。 然后将文件名作为参数传递给Job:

JobExecution jobExecution = jobLauncher.run(importUserJob, new JobParametersBuilder()
                .addString("fullPathFileName", fileToImport.getAbsolutePath())
                .toJobParameters());

最后 importReader 使用参数 fullPathFileName 来加载用户上传的文件:

      @Bean
      public FlatFileItemReader<Person> importReader(@Value("#{jobParameters[fullPathFileName]}") String pathToFile) {
        FlatFileItemReader<Person> reader = new FlatFileItemReader<>();
        reader.setResource(new FileSystemResource(pathToFile));

这里有完整的代码(未经测试,但它包含大部分组件)给你一个想法:

@Configuration
@EnableBatchProcessing
public class BatchConfig{

    @Bean
    public ResourcelessTransactionManager batchTransactionManager(){
        ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager();
        return transactionManager;
    }

    @Bean
    protected JobRepository jobRepository(ResourcelessTransactionManager batchTransactionManager) throws Exception{
        MapJobRepositoryFactoryBean jobRepository = new MapJobRepositoryFactoryBean();
        jobRepository.setTransactionManager(batchTransactionManager);
        return (JobRepository)jobRepository.getObject();
    }

    @Bean
    public JobLauncher jobLauncher(JobRepository jobRepository) throws Exception {
        SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
        jobLauncher.setJobRepository(jobRepository);
        return jobLauncher;
    }

}

@Configuration
public class ImportJobConfig {

    @Bean
    public FlatFileItemReader<Person> importReader(@Value("#{jobParameters[fullPathFileName]}") String pathToFile) {
        FlatFileItemReader<Person> reader = new FlatFileItemReader<>();
        reader.setResource(new FileSystemResource(pathToFile));
        reader.setLineMapper(new DefaultLineMapper<Person>() {{
            setLineTokenizer(new DelimitedLineTokenizer() {{
                setNames(new String[]{"firstName", "lastName"});
            }});
            setFieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
                setTargetType(Person.class);
            }});
        }});
        return reader;
    }

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

    @Bean
    public JdbcBatchItemWriter<Person> writer() {
        JdbcBatchItemWriter<Person> writer = new JdbcBatchItemWriter<>();
        writer.setItemSqlParameterSourceProvider(
                new BeanPropertyItemSqlParameterSourceProvider<Person>());
        writer.setSql("INSERT INTO people (first_name, last_name) VALUES (:firstName, :lastName)");
        writer.setDataSource(dataSource);
        return writer;
    }
    // end::readerwriterprocessor[]

    // tag::jobstep[]
    @Bean
    public Job importUserJob(JobCompletionNotificationListener listener) {
        return jobBuilderFactory.get("importUserJob").incrementer(new RunIdIncrementer())
                .listener(listener).flow(step1()).end().build();
    }

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

}

@RestController
public class MyImportController {

    @Autowired private JobLauncher jobLauncher;
    @Autowired private Job importUserJob;

    @RequestMapping(value="/import/file", method=RequestMethod.POST)
    public String create(@RequestParam("file") MultipartFile multipartFile) throws IOException{

        //Save multipartFile file in a temporary physical folder
        String path = new ClassPathResource("tmpuploads/").getURL().getPath();//it's assumed you have a folder called tmpuploads in the resources folder
        File fileToImport = new File(path + multipartFile.getOriginalFilename());
        OutputStream outputStream = new FileOutputStream(fileToImport);
        IOUtils.copy(multipartFile.getInputStream(), outputStream);
        outputStream.flush();
        outputStream.close();       

        //Launch the Batch Job
        JobExecution jobExecution = jobLauncher.run(importUserJob, new JobParametersBuilder()
                .addString("fullPathFileName", fileToImport.getAbsolutePath())
                .toJobParameters());        

        return "OK";
    }

}

【讨论】:

  • 太棒了,成功了!在这里和那里做了一些改变。编辑了答案中的更改。再次感谢。
  • @Priyanka 在这里做得很好。一个问题:如何防止作业在应用启动时触发,但仅限于控制器内?
  • 嗨@brankoterzic,要在应用启动时禁用作业执行,请在您的 application.properties "spring.batch.job.enabled=false" 中引入这一行
【解决方案2】:

我通过混合 Spring MVC (RestController) 和 Spring Batch 来做到这一点。 Spring MVC 帮助将 csv 文件作为多部分请求上传。然后我通过将此上传的 CSV 传递给 Spring Job 来异步调用 Spring 批处理。一旦 Spring 作业收到 csv 文件,它就会通过读取、处理和写入 DB 作业来进行 Spring 批处理。

【讨论】:

  • 你能分享示例代码来实现这一点吗,比如那里有什么而不​​是文件名? @Bean public FlatFileItemReader reader() { FlatFileItemReader reader = new FlatFileItemReader(); reader.setResource(new ClassPathResource("sample-data.csv")); ------ }});返回读者; }
  • @Michael,你能说得更具体点吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多