javascript
Spring批处理CSV处理
總覽
我們將討論的主題包括使用Spring Batch進(jìn)行批處理的基本概念,以及如何將數(shù)據(jù)從CSV導(dǎo)入數(shù)據(jù)庫(kù)。
0 – Spring Batch CSV處理示例應(yīng)用程序
我們正在構(gòu)建一個(gè)應(yīng)用程序,該應(yīng)用程序演示Spring Batch處理CSV文件的基礎(chǔ)。 我們的演示應(yīng)用程序?qū)⒃试S我們處理CSV文件,其中包含數(shù)百條日本動(dòng)漫標(biāo)題的記錄。
0.1 – CSV
我已經(jīng)從這個(gè)Github存儲(chǔ)庫(kù)中下載了將要使用的CSV文件,它提供了相當(dāng)全面的動(dòng)漫列表。
這是在Microsoft Excel中打開(kāi)的CSV的屏幕截圖
查看并從 Github 下載代碼
1 –項(xiàng)目結(jié)構(gòu)
2 –項(xiàng)目依賴(lài)性
除了典型的Spring Boot依賴(lài)關(guān)系外,我們還包括spring-boot-starter-batch(這是對(duì)Spring Batch的依賴(lài),顧名思義)和hsqldb(用于內(nèi)存數(shù)據(jù)庫(kù))。 我們還包括ToStringBuilder的commons-lang3。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.michaelcgood</groupId><artifactId>michaelcgood-spring-batch-csv</artifactId><version>0.0.1</version><packaging>jar</packaging><name>michaelcgood-spring-batch-csv</name><description>Michael C Good - Spring Batch CSV Example Application</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.7.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-batch</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.hsqldb</groupId><artifactId>hsqldb</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.6</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>3 –模型
這是對(duì)動(dòng)漫領(lǐng)域進(jìn)行建模的POJO。 字段是:
- ID。 為了簡(jiǎn)單起見(jiàn),我們將ID視為字符串。 但是,可以將其更改為其他數(shù)據(jù)類(lèi)型,例如Integer或Long。
- 標(biāo)題。 這是動(dòng)畫(huà)的標(biāo)題,適合作為String。
- 描述。 這是動(dòng)漫的描述,比標(biāo)題長(zhǎng),也可以視為字符串。
需要注意的是我們的三個(gè)字段的類(lèi)構(gòu)造函數(shù):public AnimeDTO(字符串id,字符串標(biāo)題,字符串描述)。 這將在我們的應(yīng)用程序中使用。 同樣,像往常一樣,我們需要?jiǎng)?chuàng)建一個(gè)沒(méi)有參數(shù)的默認(rèn)構(gòu)造函數(shù),否則Java會(huì)拋出錯(cuò)誤。
package com.michaelcgood;import org.apache.commons.lang3.builder.ToStringBuilder; /*** Contains the information of a single anime** @author Michael C Good michaelcgood.com*/public class AnimeDTO {public String getId() {return id;}public void setId(String id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}private String id;private String title;private String description;public AnimeDTO(){}public AnimeDTO(String id, String title, String description){this.id = id;this.title = title;this.description = title;}@Overridepublic String toString() {return new ToStringBuilder(this).append("id", this.id).append("title", this.title).append("description", this.description).toString();}}4 – CSV文件到數(shù)據(jù)庫(kù)配置
該類(lèi)中發(fā)生了很多事情,并且不是一次編寫(xiě)的,因此我們將逐步學(xué)習(xí)代碼。 訪(fǎng)問(wèn)Github以查看完整的代碼。
4.1 –讀者
如Spring Batch文檔所述,FlatFileIteamReader將“從平面文件中讀取數(shù)據(jù)行,這些文件通常描述記錄的數(shù)據(jù)字段由文件中的固定位置定義或由某些特殊字符(例如,逗號(hào))分隔”。
我們正在處理CSV,因此,當(dāng)然用逗號(hào)分隔數(shù)據(jù),這使其非常適合與我們的文件一起使用。
@Beanpublic FlatFileItemReader<AnimeDTO> csvAnimeReader(){FlatFileItemReader<AnimeDTO> reader = new FlatFileItemReader<AnimeDTO>();reader.setResource(new ClassPathResource("animescsv.csv"));reader.setLineMapper(new DefaultLineMapper<AnimeDTO>() {{setLineTokenizer(new DelimitedLineTokenizer() {{setNames(new String[] { "id", "title", "description" });}});setFieldSetMapper(new BeanWrapperFieldSetMapper<AnimeDTO>() {{setTargetType(AnimeDTO.class);}});}});return reader;}重要事項(xiàng):
- FlatFileItemReader使用模型進(jìn)行參數(shù)化。
4.2 –處理器
如果要在將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)之前對(duì)其進(jìn)行轉(zhuǎn)換,則需要一個(gè)ItemProcessor。 我們的代碼實(shí)際上并沒(méi)有應(yīng)用任何業(yè)務(wù)邏輯來(lái)轉(zhuǎn)換數(shù)據(jù),但是我們?cè)试S這種能力。
4.2.1 – CsvFileToDatabaseConfig.Java中的處理器
csvAnimeProcessor返回AnimeProcessor對(duì)象的新實(shí)例,我們將在下面進(jìn)行檢查。
@BeanItemProcessor<AnimeDTO, AnimeDTO> csvAnimeProcessor() {return new AnimeProcessor();}4.2.2 – AnimeProcessor.Java
如果我們想在寫(xiě)入數(shù)據(jù)庫(kù)之前應(yīng)用業(yè)務(wù)邏輯,則可以在寫(xiě)入數(shù)據(jù)庫(kù)之前操縱字符串。 例如,您可以在getTitle之后添加toUpperCase()以使標(biāo)題大寫(xiě),然后再寫(xiě)入數(shù)據(jù)庫(kù)。 但是,我決定不對(duì)此示例處理器執(zhí)行此操作或不應(yīng)用任何其他業(yè)務(wù)邏輯,因此未進(jìn)行任何操作。 該處理器僅在此處進(jìn)行演示。
package com.michaelcgood;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import org.springframework.batch.item.ItemProcessor;public class AnimeProcessor implements ItemProcessor<AnimeDTO, AnimeDTO> {private static final Logger log = LoggerFactory.getLogger(AnimeProcessor.class);@Overridepublic AnimeDTO process(final AnimeDTO AnimeDTO) throws Exception {final String id = AnimeDTO.getId();final String title = AnimeDTO.getTitle();final String description = AnimeDTO.getDescription();final AnimeDTO transformedAnimeDTO = new AnimeDTO(id, title, description);log.info("Converting (" + AnimeDTO + ") into (" + transformedAnimeDTO + ")");return transformedAnimeDTO;}}4.3 –作家
csvAnimeWriter方法負(fù)責(zé)將值實(shí)際寫(xiě)入我們的數(shù)據(jù)庫(kù)。 我們的數(shù)據(jù)庫(kù)是內(nèi)存中的HSQLDB,但是此應(yīng)用程序使我們可以輕松地將一個(gè)數(shù)據(jù)庫(kù)換成另一個(gè)數(shù)據(jù)庫(kù)。 dataSource是自動(dòng)連線(xiàn)的。
@Beanpublic JdbcBatchItemWriter<AnimeDTO> csvAnimeWriter() {JdbcBatchItemWriter<AnimeDTO> excelAnimeWriter = new JdbcBatchItemWriter<AnimeDTO>();excelAnimeWriter.setItemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<AnimeDTO>());excelAnimeWriter.setSql("INSERT INTO animes (id, title, description) VALUES (:id, :title, :description)");excelAnimeWriter.setDataSource(dataSource);return excelAnimeWriter;}4.4 –步驟
步驟是一個(gè)域?qū)ο?#xff0c;它包含批處理作業(yè)的獨(dú)立順序階段,并包含定義和控制實(shí)際批處理所需的所有信息。
現(xiàn)在,我們已經(jīng)為數(shù)據(jù)創(chuàng)建了讀取器和處理器,我們需要編寫(xiě)數(shù)據(jù)。 對(duì)于讀取,我們一直在使用面向塊的處理,這意味著我們一次讀取了一個(gè)數(shù)據(jù)。 面向塊的處理還包括在事務(wù)邊界內(nèi)創(chuàng)建將被寫(xiě)出的“塊”。 對(duì)于面向塊的處理,您可以設(shè)置提交間隔,一旦讀取的項(xiàng)目數(shù)等于已設(shè)置的提交間隔,就可以通過(guò)ItemWriter寫(xiě)入整個(gè)塊,并提交事務(wù)。 我們將塊間隔大小設(shè)置為1。
我建議閱讀有關(guān)面向塊處理的Spring Batch文檔 。
然后,讀取器,處理器和寫(xiě)入器調(diào)用我們編寫(xiě)的方法。
@Beanpublic Step csvFileToDatabaseStep() {return stepBuilderFactory.get("csvFileToDatabaseStep").<AnimeDTO, AnimeDTO>chunk(1).reader(csvAnimeReader()).processor(csvAnimeProcessor()).writer(csvAnimeWriter()).build();}4.5 –工作
作業(yè)由步驟組成。 我們將參數(shù)傳遞到下面的Job中,因?yàn)槲覀兿敫橨ob的完成情況。
@BeanJob csvFileToDatabaseJob(JobCompletionNotificationListener listener) {return jobBuilderFactory.get("csvFileToDatabaseJob").incrementer(new RunIdIncrementer()).listener(listener).flow(csvFileToDatabaseStep()).end().build();}5 –作業(yè)完成通知監(jiān)聽(tīng)器
下面的類(lèi)自動(dòng)連接JdbcTemplate,因?yàn)槲覀円呀?jīng)設(shè)置了dataSource并且我們想輕松地進(jìn)行查詢(xún)。 我們查詢(xún)的結(jié)果是AnimeDTO對(duì)象的列表。 對(duì)于返回的每個(gè)對(duì)象,我們將在控制臺(tái)中創(chuàng)建一條消息,以顯示該項(xiàng)目已被寫(xiě)入數(shù)據(jù)庫(kù)。
package com.michaelcgood;import java.sql.ResultSet; import java.sql.SQLException; import java.util.List;import org.slf4j.Logger; import org.slf4j.LoggerFactory;import org.springframework.batch.core.BatchStatus; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.listener.JobExecutionListenerSupport; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component;@Component public class JobCompletionNotificationListener extends JobExecutionListenerSupport {private static final Logger log = LoggerFactory.getLogger(JobCompletionNotificationListener.class);private final JdbcTemplate jdbcTemplate;@Autowiredpublic JobCompletionNotificationListener(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@Overridepublic void afterJob(JobExecution jobExecution) {if(jobExecution.getStatus() == BatchStatus.COMPLETED) {log.info("============ JOB FINISHED ============ Verifying the results....\n");List<AnimeDTO> results = jdbcTemplate.query("SELECT id, title, description FROM animes", new RowMapper<AnimeDTO>() {@Overridepublic AnimeDTO mapRow(ResultSet rs, int row) throws SQLException {return new AnimeDTO(rs.getString(1), rs.getString(2), rs.getString(3));}});for (AnimeDTO AnimeDTO : results) {log.info("Discovered <" + AnimeDTO + "> in the database.");}}}}6 – SQL
我們需要為我們的數(shù)據(jù)庫(kù)創(chuàng)建一個(gè)模式。 如前所述,我們已將所有字段都設(shè)置為字符串,以便于使用,因此我們將其數(shù)據(jù)類(lèi)型設(shè)置為VARCHAR。
DROP TABLE animes IF EXISTS; CREATE TABLE animes (id VARCHAR(10),title VARCHAR(400),description VARCHAR(999) );6 –主
這是帶有main()的標(biāo)準(zhǔn)類(lèi)。 如Spring文檔所述, @SpringBootApplication是一個(gè)方便注釋,其中包括@ Configuration , @EnableAutoConfiguration , @ EnableWebMvc和@ComponentScan 。
package com.michaelcgood;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication public class SpringBatchCsvApplication {public static void main(String[] args) {SpringApplication.run(SpringBatchCsvApplication.class, args);} }7 –演示
7.1 –轉(zhuǎn)換
FieldSet通過(guò)處理器輸入,“ Converting”被打印到控制臺(tái)。
7.2 –在數(shù)據(jù)庫(kù)中發(fā)現(xiàn)新項(xiàng)目
當(dāng)Spring Batch Job完成時(shí),我們選擇所有記錄并將它們分別打印到控制臺(tái)。
7.3 –批處理完成
批處理完成后,這就是打印到控制臺(tái)的內(nèi)容。
Job: [FlowJob: [name=csvFileToDatabaseJob]] completed with the following parameters: [{run.id=1, -spring.output.ansi.enabled=always}] and the following status: [COMPLETED] Started SpringBatchCsvApplication in 36.0 seconds (JVM running for 46.616)8 –結(jié)論
Spring Batch建立在基于POJO的開(kāi)發(fā)方法和Spring Framework的用戶(hù)友好性的基礎(chǔ)上,使開(kāi)發(fā)人員可以輕松地創(chuàng)建企業(yè)級(jí)批處理。
源代碼在 Github上
翻譯自: https://www.javacodegeeks.com/2017/10/spring-batch-csv-processing.html
總結(jié)
以上是生活随笔為你收集整理的Spring批处理CSV处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 游戏《星空》发布未来更新计划:正进行性能
- 下一篇: 什么是JavaServer Faces(