Converted to asciidoctor
This commit is contained in:
@@ -26,3 +26,4 @@ build/
|
||||
# Spring Bootstrap artifacts
|
||||
|
||||
dependency-reduced-pom.xml
|
||||
README.html
|
||||
|
||||
+86
-53
@@ -1,86 +1,116 @@
|
||||
<#assign project_id="gs-batch-processing">
|
||||
:spring_boot_version: 0.5.0.M6
|
||||
:Component: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Component.html
|
||||
:EnableAutoConfiguration: http://docs.spring.io/spring-boot/docs/{spring_boot_version}/api/org/springframework/boot/autoconfigure/EnableAutoConfiguration.html
|
||||
:SpringApplication: http://docs.spring.io/spring-boot/docs/{spring_boot_version}/api/org/springframework/boot/SpringApplication.html
|
||||
:toc:
|
||||
:icons: font
|
||||
:source-highlighter: prettify
|
||||
:project_id: gs-batch-processing
|
||||
This guide walks you through the process of creating a basic batch-driven solution.
|
||||
|
||||
What you'll build
|
||||
-----------------
|
||||
== What you'll build
|
||||
|
||||
You'll build a service that imports data from a CSV spreadsheet, transforms it with custom code, and stores the final results in a database.
|
||||
|
||||
|
||||
What you'll need
|
||||
----------------
|
||||
== What you'll need
|
||||
|
||||
- About 15 minutes
|
||||
- <@prereq_editor_jdk_buildtools/>
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/prereq_editor_jdk_buildtools.adoc[]
|
||||
|
||||
## <@how_to_complete_this_guide jump_ahead='Create a business class'/>
|
||||
:jump_ahead: Create a business class
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/how_to_complete_this_guide.adoc[]
|
||||
|
||||
|
||||
<a name="scratch"></a>
|
||||
Set up the project
|
||||
------------------
|
||||
<@build_system_intro/>
|
||||
[[scratch]]
|
||||
== Set up the project
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/build_system_intro.adoc[]
|
||||
|
||||
<@create_directory_structure_hello/>
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/create_directory_structure_hello.adoc[]
|
||||
|
||||
|
||||
<@create_both_builds/>
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/create_both_builds.adoc[]
|
||||
|
||||
<@bootstrap_starter_pom_disclaimer/>
|
||||
`build.gradle`
|
||||
// AsciiDoc source formatting doesn't support groovy, so using java instead
|
||||
[source,java]
|
||||
----
|
||||
include::initial/build.gradle[]
|
||||
----
|
||||
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/bootstrap_starter_pom_disclaimer.adoc[]
|
||||
|
||||
### Create business data
|
||||
|
||||
Typically your customer or a business analyst supplies a spreadsheet. In this case, you make it up.
|
||||
|
||||
<@snippet path="src/main/resources/sample-data.csv" prefix="initial"/>
|
||||
`src/main/resources/sample-data.csv`
|
||||
[source,csv]
|
||||
----
|
||||
include::initial/src/main/resources/sample-data.csv[]
|
||||
----
|
||||
|
||||
This spreadsheet contains a first name and a last name on each row, separated by a comma. This is a fairly common pattern that Spring handles out-of-the-box, as you will see.
|
||||
|
||||
### Define the destination for your data
|
||||
|
||||
Next, you write a SQL script to create a table to store the data.
|
||||
|
||||
<@snippet path="src/main/resources/schema-all.sql" prefix="initial"/>
|
||||
`src/main/resources/schema-all.sql`
|
||||
[source,sql]
|
||||
----
|
||||
include::initial/src/main/resources/schema-all.sql[]
|
||||
----
|
||||
|
||||
> **Note:** Spring Boot runs `schema-@@platform@@.sql` automatically during startup. `-all` is the default for all platforms.
|
||||
NOTE: Spring Boot runs `schema-@@platform@@.sql` automatically during startup. `-all` is the default for all platforms.
|
||||
|
||||
|
||||
<a name="initial"></a>
|
||||
Create a business class
|
||||
-----------------------
|
||||
[[initial]]
|
||||
== Create a business class
|
||||
|
||||
Now that you see the format of data inputs and outputs, you write code to represent a row of data.
|
||||
|
||||
<@snippet path="src/main/java/hello/Person.java" prefix="complete"/>
|
||||
`src/main/java/hello/Person.java`
|
||||
[source,java]
|
||||
----
|
||||
include::complete/src/main/java/hello/Person.java[]
|
||||
----
|
||||
|
||||
You can instantiate the `Person` class either with first and last name through a constructor, or by setting the properties.
|
||||
|
||||
|
||||
Create an intermediate processor
|
||||
--------------------------------
|
||||
== Create an intermediate processor
|
||||
|
||||
A common paradigm in batch processing is to ingest data, transform it, and then pipe it out somewhere else. Here you write a simple transformer that converts the names to uppercase.
|
||||
|
||||
<@snippet path="src/main/java/hello/PersonItemProcessor.java" prefix="complete"/>
|
||||
`src/main/java/hello/PersonItemProcessor.java`
|
||||
[source,java]
|
||||
----
|
||||
include::complete/src/main/java/hello/PersonItemProcessor.java[]
|
||||
----
|
||||
|
||||
`PersonItemProcessor` implements Spring Batch's `ItemProcessor` interface. This makes it easy to wire the code into a batch job that you define further down in this guide. According to the interface, you receive an incoming `Person` object, after which you transform it to an upper-cased `Person`.
|
||||
|
||||
> **Note:** There is no requirement that the input and output types be the same. In fact, after one source of data is read, sometimes the application's data flow needs a different data type.
|
||||
NOTE: There is no requirement that the input and output types be the same. In fact, after one source of data is read, sometimes the application's data flow needs a different data type.
|
||||
|
||||
|
||||
Put together a batch job
|
||||
------------------------
|
||||
== Put together a batch job
|
||||
|
||||
Now you put together the actual batch job. Spring Batch provides many utility classes that reduce the need to write custom code. Instead, you can focus on the business logic.
|
||||
|
||||
<@snippet path="src/main/java/hello/BatchConfiguration.java" prefix="complete"/>
|
||||
`src/main/java/hello/BatchConfiguration.java`
|
||||
[source,java]
|
||||
----
|
||||
include::complete/src/main/java/hello/BatchConfiguration.java[]
|
||||
----
|
||||
|
||||
For starters, the `@EnableBatchProcessing` annotation adds many critical beans that support jobs and saves you a lot of leg work. This example uses a memory-based database (provided by `@EnableBatchProcessing`), meaning that when it's done, the data is gone.
|
||||
|
||||
Break it down:
|
||||
|
||||
<@snippet "src/main/java/hello/BatchConfiguration.java" "readerwriterprocessor" "/complete"/>
|
||||
|
||||
`src/main/java/hello/BatchConfiguration.java`
|
||||
[source,java]
|
||||
----
|
||||
include::/complete/src/main/java/hello/BatchConfiguration.java[tag=readerwriterprocessor]
|
||||
----
|
||||
.
|
||||
The first chunk of code defines the input, processor, and output.
|
||||
- `reader()` creates an `ItemReader`. It looks for a file called `sample-data.csv` and parses each line item with enough information to turn it into a `Person`.
|
||||
- `processor()` creates an instance of our `PersonItemProcessor` you defined earlier, meant to uppercase the data.
|
||||
@@ -88,43 +118,50 @@ The first chunk of code defines the input, processor, and output.
|
||||
|
||||
The next chunk focuses on the actual job configuration.
|
||||
|
||||
<@snippet "src/main/java/hello/BatchConfiguration.java" "jobstep" "/complete"/>
|
||||
|
||||
`src/main/java/hello/BatchConfiguration.java`
|
||||
[source,java]
|
||||
----
|
||||
include::/complete/src/main/java/hello/BatchConfiguration.java[tag=jobstep]
|
||||
----
|
||||
.
|
||||
The first method defines the job and the second one defines a single step. Jobs are built from steps, where each step can involve a reader, a processor, and a writer.
|
||||
|
||||
In this job definition, you need an incrementer because jobs use a database to maintain execution state. You then list each step, of which this job has only one step. The job ends, and the Java API produces a perfectly configured job.
|
||||
|
||||
In the step definition, you define how much data to write at a time. In this case, it writes up to ten records at a time. Next, you configure the reader, processor, and writer using the injected bits from earlier.
|
||||
|
||||
> **Note:** chunk() is prefixed `<Person,Person>` because it's a generic method. This represents the input and output types of each "chunk" of processing, and lines up with `ItemReader<Person>` and `ItemWriter<Person>`.
|
||||
NOTE: chunk() is prefixed `<Person,Person>` because it's a generic method. This represents the input and output types of each "chunk" of processing, and lines up with `ItemReader<Person>` and `ItemWriter<Person>`.
|
||||
|
||||
|
||||
Make the application executable
|
||||
-------------------------------
|
||||
== Make the application executable
|
||||
|
||||
Although batch processing can be embedded in web apps and WAR files, the simpler approach demonstrated below creates a standalone application. You package everything in a single, executable JAR file, driven by a good old Java `main()` method.
|
||||
|
||||
### Create an Application class
|
||||
|
||||
<@snippet path="src/main/java/hello/Application.java" prefix="complete"/>
|
||||
`src/main/java/hello/Application.java`
|
||||
[source,java]
|
||||
----
|
||||
include::complete/src/main/java/hello/Application.java[]
|
||||
----
|
||||
|
||||
The `main()` method defers to the [`SpringApplication`][] helper class, providing `Application.class` as an argument to its `run()` method. This tells Spring to read the annotation metadata from `Application` and to manage it as a component in the [Spring application context][u-application-context].
|
||||
The `main()` method defers to the {SpringApplication}[`SpringApplication`] helper class, providing `Application.class` as an argument to its `run()` method. This tells Spring to read the annotation metadata from `Application` and to manage it as a component in the link:/understanding/application-context[Spring application context].
|
||||
|
||||
The `@ComponentScan` annotation tells Spring to search recursively through the `hello` package and its children for classes marked directly or indirectly with Spring's [`@Component`][] annotation. This directive ensures that Spring finds and registers `BatchConfiguration`, because it is marked with `@Configuration`, which in turn is a kind of `@Component` annotation.
|
||||
The `@ComponentScan` annotation tells Spring to search recursively through the `hello` package and its children for classes marked directly or indirectly with Spring's {Component}[`@Component`] annotation. This directive ensures that Spring finds and registers `BatchConfiguration`, because it is marked with `@Configuration`, which in turn is a kind of `@Component` annotation.
|
||||
|
||||
The [`@EnableAutoConfiguration`][] annotation switches on reasonable default behaviors based on the content of your classpath. For example, it looks for any class that implements the `CommandLineRunner` interface and invokes its `run()` method. In this case, it runs the demo code for this guide.
|
||||
The {EnableAutoConfiguration}[`@EnableAutoConfiguration`] annotation switches on reasonable default behaviors based on the content of your classpath. For example, it looks for any class that implements the `CommandLineRunner` interface and invokes its `run()` method. In this case, it runs the demo code for this guide.
|
||||
|
||||
For demonstration purposes, there is code to create a `JdbcTemplate`, query the database, and print out the names of people the batch job inserts.
|
||||
|
||||
<@build_an_executable_jar_subhead/>
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/build_an_executable_jar_subhead.adoc[]
|
||||
|
||||
<@build_an_executable_jar_with_both/>
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/build_an_executable_jar_with_both.adoc[]
|
||||
|
||||
<@run_the_application_with_both module="batch job"/>
|
||||
:module: batch job
|
||||
include::https://raw.github.com/spring-guides/getting-started-macros/master/run_the_application_with_both.adoc[]
|
||||
|
||||
The job prints out a line for each person that gets transformed. After the job runs, you can also see the output from querying the database.
|
||||
|
||||
```sh
|
||||
....
|
||||
Converting (firstName: Jill, lastName: Doe) into (firstName: JILL, lastName: DOE)
|
||||
Converting (firstName: Joe, lastName: Doe) into (firstName: JOE, lastName: DOE)
|
||||
Converting (firstName: Justin, lastName: Doe) into (firstName: JUSTIN, lastName: DOE)
|
||||
@@ -135,16 +172,12 @@ Found <firstName: JOE, lastName: DOE> in the database.
|
||||
Found <firstName: JUSTIN, lastName: DOE> in the database.
|
||||
Found <firstName: JANE, lastName: DOE> in the database.
|
||||
Found <firstName: JOHN, lastName: DOE> in the database.
|
||||
```
|
||||
....
|
||||
|
||||
|
||||
Summary
|
||||
-------
|
||||
== Summary
|
||||
|
||||
Congratulations! You built a batch job that ingested data from a spreadsheet, processed it, and wrote it to a database.
|
||||
|
||||
|
||||
[`SpringApplication`]: http://docs.spring.io/spring-boot/docs/0.5.0.M3/api/org/springframework/boot/SpringApplication.html
|
||||
[`@EnableAutoConfiguration`]: http://docs.spring.io/spring-boot/docs/0.5.0.M3/api/org/springframework/boot/autoconfigure/EnableAutoConfiguration.html
|
||||
[`@Component`]: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Component.html
|
||||
|
||||
@@ -26,7 +26,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@EnableBatchProcessing
|
||||
public class BatchConfiguration {
|
||||
|
||||
// {!begin readerwriterprocessor}
|
||||
// tag::readerwriterprocessor[]
|
||||
@Bean
|
||||
public ItemReader<Person> reader() {
|
||||
FlatFileItemReader<Person> reader = new FlatFileItemReader<Person>();
|
||||
@@ -55,9 +55,9 @@ public class BatchConfiguration {
|
||||
writer.setDataSource(dataSource);
|
||||
return writer;
|
||||
}
|
||||
// {!end readerwriterprocessor}
|
||||
// end::readerwriterprocessor[]
|
||||
|
||||
// {!begin jobstep}
|
||||
// tag::jobstep[]
|
||||
@Bean
|
||||
public Job importUserJob(JobBuilderFactory jobs, Step s1) {
|
||||
return jobs.get("importUserJob")
|
||||
@@ -77,7 +77,7 @@ public class BatchConfiguration {
|
||||
.writer(writer)
|
||||
.build();
|
||||
}
|
||||
// {!end jobstep}
|
||||
// end::jobstep[]
|
||||
|
||||
@Bean
|
||||
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
|
||||
|
||||
Reference in New Issue
Block a user