基於Spring Batch的Spring Boot的教程 - Baeldung

banq發表於2020-12-12

在本文中,我們將學習如何使用Spring Boot建立一個簡單的Spring Batch作業。首先,我們從定義一些基本配置開始。然後,我們將看到如何新增檔案讀取器和資料庫寫入器。最後,如何應用一些自定義處理並檢查我們的作業是否成功執行。
可以在GitHub上獲得本文的完整原始碼 。
 
首先,讓我們將spring-boot-starter-batch新增到我們的 pom.xml中:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-batch</artifactId>
    <version>2.4.0.RELEASE</version>
</dependency>

我們還將新增org.hsqldb依賴關係,該依賴關係也可以從Maven Central 獲得:

<dependency>
    <groupId>org.hsqldb</groupId>
    <artifactId>hsqldb</artifactId>
    <version>2.5.1</version>
    <scope>runtime</scope>
</dependency>

 
我們將構建一個工作,該工作將從CSV檔案匯入咖啡清單,使用自定義處理器對其進行轉換,然後將最終結果儲存在記憶體資料庫中。
讓我們從定義應用程式入口點開始:

@SpringBootApplication
public class SpringBootBatchProcessingApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootBatchProcessingApplication.class, args);
    }
}

如我們所見,這是一個標準的Spring Boot應用程式。由於我們希望在可能的情況下使用預設配置值,因此我們將使用一組非常簡單的應用程式配置屬性。

我們將在src / main / resources / application.properties檔案中定義以下屬性:

file.input=coffee-list.csv

此屬性包含我們輸入的咖啡清單的位置。每行都包含我們咖啡的品牌,來源和一些特徵:

Blue Mountain,Jamaica,Fruity
Lavazza,Colombia,Strong
Folgers,America,Smokey

我們將看到,這是一個平面CSV檔案,這意味著Spring可以在不進行任何特殊自定義的情況下對其進行處理。

接下來,我們將新增一個SQL指令碼schema-all.sql來建立我們的咖啡桌來儲存資料:

DROP TABLE coffee IF EXISTS;

CREATE TABLE coffee  (
    coffee_id BIGINT IDENTITY NOT NULL PRIMARY KEY,
    brand VARCHAR(20),
    origin VARCHAR(20),
    characteristics VARCHAR(30)
);

通常,Spring Boot將在啟動過程中自動執行此指令碼。
隨後,我們將需要一個簡單的域類來儲存我們的咖啡專案:

public class Coffee {

    private String brand;
    private String origin;
    private String characteristics;

    public Coffee(String brand, String origin, String characteristics) {
        this.brand = brand;
        this.origin = origin;
        this.characteristics = characteristics;
    }

    // getters and setters
}

如前所述,我們的Coffee物件包含三個屬性。
現在,到關鍵部分,我們的工作配置。我們將逐步進行,建立我們的配置並解釋其中的每個部分:

@Configuration
@EnableBatchProcessing
public class BatchConfiguration {

    @Autowired
    public JobBuilderFactory jobBuilderFactory;

    @Autowired
    public StepBuilderFactory stepBuilderFactory;
    
    @Value("${file.input}")
    private String fileInput;
    
    // ...
}

首先,我們從標準的Spring @Configuration類開始。接下來,我們在類中新增 @EnableBatchProcessing批註。值得注意的是,這使我們能夠使用許多有用的bean來支援工作,並將節省大量的日常工作。

此外,使用此註釋還使我們可以訪問兩個有用的工廠,稍後將在構建作業配置和作業步驟時使用它們。

對於初始配置的最後一部分,我們包括對先前宣告的file.input屬性的引用。
現在,我們可以繼續在配置中定義一個閱讀器bean:

@Bean
public FlatFileItemReader reader() {
    return new FlatFileItemReaderBuilder().name("coffeeItemReader")
      .resource(new ClassPathResource(fileInput))
      .delimited()
      .names(new String[] { "brand", "origin", "characteristics" })
      .fieldSetMapper(new BeanWrapperFieldSetMapper() {{
          setTargetType(Coffee.class);
      }})
      .build();
}

簡而言之,上面定義的閱讀器bean將查詢名為coffee-list.csv的檔案,並將每個訂單項解析為Coffee物件。

同樣,我們定義一個writer bean:

@Bean
public JdbcBatchItemWriter writer(DataSource dataSource) {
    return new JdbcBatchItemWriterBuilder()
      .itemSqlParameterSourceProvider(new BeanPropertyItemSqlParameterSourceProvider<>())
      .sql("INSERT INTO coffee (brand, origin, characteristics) VALUES (:brand, :origin, :characteristics)")
      .dataSource(dataSource)
      .build();
}

這次,我們包含了由咖啡物件的Java Bean屬性驅動的將單個咖啡專案插入到資料庫中所需的SQL語句。方便地,透過@EnableBatchProcessing批註自動建立dataSource。
最後,我們需要新增實際的作業步驟和配置:

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

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

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

如我們所見,我們的工作相對簡單,由step1方法中定義的一個步驟組成。

讓我們看一下此步驟正在執行的操作:
  • 首先,我們配置步驟,以便使用chunk(10)宣告一次最多寫入十條記錄
  • 然後,我們使用讀取器bean讀取咖啡資料,該讀取器bean是使用reader方法設定的
  • 接下來,我們將每個咖啡專案傳遞給自定義處理器,在其中應用一些自定義業務邏輯
  • 最後,我們使用之前看到的編寫器將每個咖啡專案寫入資料庫

另一方面,我們的importUserJob包含我們的作業定義,其中包含使用內建RunIdIncrementer類的ID 。我們還設定了一個JobCompletionNotificationListener,用來在作業完成時得到通知。

為了完成我們的作業配置,我們列出了每個步驟(儘管此作業只有一個步驟)。現在,我們已經完成了完美的配置!
 
讓我們詳細瞭解一下我們先前在作業配置中定義的自定義處理器:

public class CoffeeItemProcessor implements ItemProcessor<Coffee, Coffee> {

    private static final Logger LOGGER = LoggerFactory.getLogger(CoffeeItemProcessor.class);

    @Override
    public Coffee process(final Coffee coffee) throws Exception {
        String brand = coffee.getBrand().toUpperCase();
        String origin = coffee.getOrigin().toUpperCase();
        String chracteristics = coffee.getCharacteristics().toUpperCase();

        Coffee transformedCoffee = new Coffee(brand, origin, chracteristics);
        LOGGER.info("Converting ( {} ) into ( {} )", coffee, transformedCoffee);

        return transformedCoffee;
    }
}

特別感興趣的是,ItemProcessor介面為我們提供了一種在作業執行期間應用某些特定業務邏輯的機制。

為了簡單起見,我們定義了CoffeeItemProcessor,它接受一個輸入Coffee物件,並將每個屬性轉換為uppercase。
此外,我們還將編寫一個JobCompletionNotificationListener, 以在工作完成時提供一些反饋:

@Override
public void afterJob(JobExecution jobExecution) {
    if (jobExecution.getStatus() == BatchStatus.COMPLETED) {
        LOGGER.info("!!! JOB FINISHED! Time to verify the results");

        String query = "SELECT brand, origin, characteristics FROM coffee";
        jdbcTemplate.query(query, (rs, row) -> new Coffee(rs.getString(1), rs.getString(2), rs.getString(3)))
          .forEach(coffee -> LOGGER.info("Found < {} > in the database.", coffee));
    }
}

在上面的示例中,我們重寫afterJob方法並檢查作業是否成功完成。此外,我們執行一個簡單查詢以檢查每個咖啡專案是否已成功儲存在資料庫中。
現在我們已經準備就緒,可以開始工作了,這是有趣的部分。讓我們繼續工作吧:

...
17:41:16.336 [main] INFO  c.b.b.JobCompletionNotificationListener -
  !!! JOB FINISHED! Time to verify the results
17:41:16.336 [main] INFO  c.b.b.JobCompletionNotificationListener -
  Found < Coffee [brand=BLUE MOUNTAIN, origin=JAMAICA, characteristics=FRUITY] > in the database.
17:41:16.337 [main] INFO  c.b.b.JobCompletionNotificationListener -
  Found < Coffee [brand=LAVAZZA, origin=COLOMBIA, characteristics=STRONG] > in the database.
17:41:16.337 [main] INFO  c.b.b.JobCompletionNotificationListener -
  Found < Coffee [brand=FOLGERS, origin=AMERICA, characteristics=SMOKEY] > in the database.
...

如我們所見,我們的工作成功執行,每項咖啡都按預期儲存在資料庫中。

相關文章