Skip to content

Commit 4cfd7fe

Browse files
author
Jay Bryant
committed
Use the Spring Initializr
I rewrote the doc and rearranged the code to use the Spring Initializr.
1 parent 77a2bd0 commit 4cfd7fe

File tree

13 files changed

+276
-183
lines changed

13 files changed

+276
-183
lines changed

README.adoc

Lines changed: 142 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66
:icons: font
77
:source-highlighter: prettify
88
:project_id: gs-batch-processing
9+
910
This guide walks you through the process of creating a basic batch-driven solution.
1011

11-
== What you'll build
12+
== What You Will build
1213

13-
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.
14+
You will build a service that imports data from a CSV spreadsheet, transforms it with custom code, and stores the final results in a database.
1415

1516

1617
== What you'll need
@@ -20,139 +21,219 @@ include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/
2021

2122
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/how_to_complete_this_guide.adoc[]
2223

23-
24-
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-gradle.adoc[]
25-
26-
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-maven.adoc[]
27-
28-
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/hide-show-sts.adoc[]
29-
3024
== Business Data
3125

32-
Typically your customer or a business analyst supplies a spreadsheet. In this case, you make it up.
26+
Typically, your customer or a business analyst supplies a spreadsheet. For this simple
27+
example, you can find some made-up data in `src/main/resources/sample-data.csv`:
3328

34-
`src/main/resources/sample-data.csv`
29+
====
3530
[source,csv]
3631
----
3732
include::initial/src/main/resources/sample-data.csv[]
3833
----
34+
====
3935

40-
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.
41-
36+
This spreadsheet contains a first name and a last name on each row, separated by a comma.
37+
This is a fairly common pattern that Spring can handle without customization.
4238

43-
Next, you write a SQL script to create a table to store the data.
39+
Next, you need to write an SQL script to create a table to store the data. You can find
40+
such a script in `src/main/resources/schema-all.sql`:
4441

45-
`src/main/resources/schema-all.sql`
42+
====
4643
[source,sql]
4744
----
4845
include::initial/src/main/resources/schema-all.sql[]
4946
----
47+
====
5048

5149
NOTE: Spring Boot runs `schema-@@platform@@.sql` automatically during startup. `-all` is the default for all platforms.
5250

51+
== Starting with Spring Initializr
52+
53+
For all Spring applications, you should start with the https://start.spring.io[Spring
54+
Initializr]. The Initializr offers a fast way to pull in all the dependencies you need for
55+
an application and does a lot of the set up for you. This example needs the Spring Batch
56+
and HyperSQL Database dependencies. The following image shows the Initializr set up for
57+
this sample project:
58+
59+
image::images/initializr.jpg[]
60+
61+
NOTE: The preceding image shows the Initializr with Maven chosen as the build tool. You
62+
can also use Gradle. It also shows values of `com.example` and
63+
`batch-processing` as the Group and Artifact, respectively. You will use those
64+
values throughout the rest of this sample.
65+
66+
The following listing shows the `pom.xml` file created when you choose Maven:
67+
68+
====
69+
[src,xml]
70+
----
71+
include::complete/pom.xml[]
72+
----
73+
====
74+
75+
The following listing shows the `build.gradle` file created when you choose Gradle:
76+
77+
====
78+
[src,groovy]
79+
----
80+
include::complete/build.gradle[]
81+
----
82+
====
5383

5484
[[initial]]
55-
== Create a business class
85+
== Create a Business Class
5686

57-
Now that you see the format of data inputs and outputs, you write code to represent a row of data.
87+
Now that you can see the format of data inputs and outputs, you can write code to
88+
represent a row of data, as the following example (from
89+
`src/main/java/com/example/batchprocessing/Person.java`) shows:
5890

59-
`src/main/java/hello/Person.java`
91+
====
6092
[source,java]
6193
----
62-
include::complete/src/main/java/hello/Person.java[]
94+
include::complete/src/main/java/com/example/batchprocessing/Person.java[]
6395
----
96+
====
6497

65-
You can instantiate the `Person` class either with first and last name through a constructor, or by setting the properties.
98+
You can instantiate the `Person` class either with first and last name through a
99+
constructor or by setting the properties.
66100

67101

68-
== Create an intermediate processor
102+
== Create an Intermediate Processor
69103

70-
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.
104+
A common paradigm in batch processing is to ingest data, transform it, and then pipe it
105+
out somewhere else. Here, you need to write a simple transformer that converts the names
106+
to uppercase. The following listing (from
107+
`src/main/java/com/example/batchprocessing/PersonItemProcessor.java`) shows how to do
108+
so:
71109

72-
`src/main/java/hello/PersonItemProcessor.java`
110+
====
73111
[source,java]
74112
----
75-
include::complete/src/main/java/hello/PersonItemProcessor.java[]
113+
include::complete/src/main/java/com/example/batchprocessing/PersonItemProcessor.java[]
76114
----
115+
====
77116

78-
`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`.
79-
80-
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.
117+
`PersonItemProcessor` implements Spring Batch's `ItemProcessor` interface. This makes it
118+
easy to wire the code into a batch job that you will define later in this guide. According
119+
to the interface, you receive an incoming `Person` object, after which you transform it to
120+
an upper-cased `Person`.
81121

122+
NOTE: The input and output types need not be the same. In fact, after one source of data
123+
is read, sometimes the application's data flow needs a different data type.
82124

83-
== Put together a batch job
125+
== Put Together a Batch Job
84126

85-
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.
127+
Now you need to put together the actual batch job. Spring Batch provides many utility
128+
classes that reduce the need to write custom code. Instead, you can focus on the business
129+
logic. The following example, (from
130+
`src/main/java/com/exampe/batchprocessing/BatchConfiguration.java`) shows how configure a
131+
batch job:
86132

87-
`src/main/java/hello/BatchConfiguration.java`
133+
====
88134
[source,java]
89135
----
90-
include::complete/src/main/java/hello/BatchConfiguration.java[]
136+
include::complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java[]
91137
----
138+
====
92139

93-
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.
140+
For starters, the `@EnableBatchProcessing` annotation adds many critical beans that
141+
support jobs and save you a lot of leg work. This example uses a memory-based database
142+
(provided by `@EnableBatchProcessing`), meaning that, when it is done, the data is gone.
143+
The following listing (from
144+
`src/main/java/com/example/batchprocessing/BatchConfiguration.java`) shows the definition
145+
of the batch job (the reader, the processor, and the writer):
94146

95-
Break it down:
96-
97-
`src/main/java/hello/BatchConfiguration.java`
147+
====
98148
[source,java]
99149
----
100-
include::/complete/src/main/java/hello/BatchConfiguration.java[tag=readerwriterprocessor]
150+
include::/complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java[tag=readerwriterprocessor]
101151
----
152+
====
102153

103154
The first chunk of code defines the input, processor, and output.
104155

105-
* `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`.
106-
* `processor()` creates an instance of our `PersonItemProcessor` you defined earlier, meant to uppercase the data.
107-
* `write(DataSource)` creates an `ItemWriter`. This one is aimed at a JDBC destination and automatically gets a copy of the dataSource created by `@EnableBatchProcessing`. It includes the SQL statement needed to insert a single `Person` driven by Java bean properties.
156+
* `reader()` creates an `ItemReader`. It looks for a file called `sample-data.csv` and
157+
parses each line item with enough information to turn it into a `Person`.
158+
* `processor()` creates an instance of the `PersonItemProcessor` that you defined earlier,
159+
meant to converth the data to upper case.
160+
* `write(DataSource)` creates an `ItemWriter`. This one is aimed at a JDBC destination and
161+
automatically gets a copy of the dataSource created by `@EnableBatchProcessing`. It
162+
includes the SQL statement needed to insert a single `Person`, driven by Java bean
163+
properties.
108164

109-
The next chunk focuses on the actual job configuration.
165+
The next chunk (from `src/main/java/com/example/batchprocessing/BatchConfiguration.java`)
166+
shows the actual job configuration:
110167

111-
`src/main/java/hello/BatchConfiguration.java`
168+
====
112169
[source,java]
113170
----
114-
include::/complete/src/main/java/hello/BatchConfiguration.java[tag=jobstep]
171+
include::/complete/src/main/java/com/example/batchprocessing/BatchConfiguration.java[tag=jobstep]
115172
----
173+
====
174+
175+
The first method defines the job, and the second one defines a single step. Jobs are built
176+
from steps, where each step can involve a reader, a processor, and a writer.
116177

117-
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.
178+
In this job definition, you need an incrementer, because jobs use a database to maintain
179+
execution state. You then list each step, (though this job has only one step). The job
180+
ends, and the Java API produces a perfectly configured job.
118181

119-
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.
182+
In the step definition, you define how much data to write at a time. In this case, it
183+
writes up to ten records at a time. Next, you configure the reader, processor, and writer
184+
by using the bits injected earlier.
120185

121-
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.
186+
NOTE: `chunk()` is prefixed `<Person,Person>` because it is a generic method. This
187+
represents the input and output types of each "`chunk`" of processing and lines up with
188+
`ItemReader<Person>` and `ItemWriter<Person>`.
122189

123-
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>`.
190+
The last bit of batch configuration is a way to get notified when the job completes. The
191+
following example (from
192+
`src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java`) shows
193+
such a class:
124194

125-
`src/main/java/hello/JobCompletionNotificationListener.java`
195+
====
126196
[source,java]
127197
----
128-
include::/complete/src/main/java/hello/JobCompletionNotificationListener.java[]
198+
include::/complete/src/main/java/com/example/batchprocessing/JobCompletionNotificationListener.java[]
129199
----
200+
====
130201

131-
This code listens for when a job is `BatchStatus.COMPLETED`, and then uses `JdbcTemplate` to inspect the results.
132-
202+
The `JobCompletionNotificationListener` listens for when a job is `BatchStatus.COMPLETED`
203+
and then uses `JdbcTemplate` to inspect the results.
133204

134-
== Make the application executable
205+
== Make the Application Executable
135206

136207
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.
137208

209+
The Spring Initializr created an application class for you. For this simple example, it
210+
works without further modification. The folowing listing (from
211+
`src/main/java/com/example/batchprocessing/BatchProcessingApplication.java`) shows the
212+
application class:
138213

139-
`src/main/java/hello/Application.java`
214+
====
140215
[source,java]
141216
----
142-
include::complete/src/main/java/hello/Application.java[]
217+
include::complete/src/main/java/com/example/batchprocessing/BatchProcessingApplication.java[]
143218
----
219+
====
144220

145-
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/spring-boot-application.adoc[]
221+
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/spring-boot-application-new-path.adoc[]
146222

147-
For demonstration purposes, there is code to create a `JdbcTemplate`, query the database, and print out the names of people the batch job inserts.
223+
For demonstration purposes, there is code to create a `JdbcTemplate`, query the database,
224+
and print out the names of people the batch job inserts.
148225

149226
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/build_an_executable_jar_subhead.adoc[]
150227

151228
include::https://raw.githubusercontent.com/spring-guides/getting-started-macros/master/build_an_executable_jar_with_both.adoc[]
152229

153-
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.
230+
The job prints out a line for each person that gets transformed. After the job runs, you
231+
can also see the output from querying the database. It should resemble the following
232+
output:
154233

155-
....
234+
====
235+
[source,text]
236+
----
156237
Converting (firstName: Jill, lastName: Doe) into (firstName: JILL, lastName: DOE)
157238
Converting (firstName: Joe, lastName: Doe) into (firstName: JOE, lastName: DOE)
158239
Converting (firstName: Justin, lastName: Doe) into (firstName: JUSTIN, lastName: DOE)
@@ -163,12 +244,13 @@ Found <firstName: JOE, lastName: DOE> in the database.
163244
Found <firstName: JUSTIN, lastName: DOE> in the database.
164245
Found <firstName: JANE, lastName: DOE> in the database.
165246
Found <firstName: JOHN, lastName: DOE> in the database.
166-
....
167-
247+
----
248+
====
168249

169250
== Summary
170251

171-
Congratulations! You built a batch job that ingested data from a spreadsheet, processed it, and wrote it to a database.
252+
Congratulations! You built a batch job that ingested data from a spreadsheet, processed
253+
it, and wrote it to a database.
172254

173255
== See also
174256

complete/build.gradle

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,20 @@
1-
buildscript {
2-
repositories {
3-
mavenCentral()
4-
}
5-
dependencies {
6-
classpath("org.springframework.boot:spring-boot-gradle-plugin:2.1.6.RELEASE")
7-
}
1+
plugins {
2+
id 'org.springframework.boot' version '2.1.8.RELEASE'
3+
id 'io.spring.dependency-management' version '1.0.8.RELEASE'
4+
id 'java'
85
}
96

10-
apply plugin: 'java'
11-
apply plugin: 'eclipse'
12-
apply plugin: 'idea'
13-
apply plugin: 'org.springframework.boot'
14-
apply plugin: 'io.spring.dependency-management'
15-
16-
bootJar {
17-
baseName = 'gs-batch-processing'
18-
version = '0.1.0'
19-
}
7+
group = 'com.example'
8+
version = '0.0.1-SNAPSHOT'
9+
sourceCompatibility = '1.8'
2010

2111
repositories {
22-
mavenCentral()
12+
mavenCentral()
2313
}
2414

25-
sourceCompatibility = 1.8
26-
targetCompatibility = 1.8
27-
2815
dependencies {
29-
compile("org.springframework.boot:spring-boot-starter-batch")
30-
compile("org.hsqldb:hsqldb")
31-
testCompile("junit:junit")
16+
implementation 'org.springframework.boot:spring-boot-starter-batch'
17+
runtimeOnly 'org.hsqldb:hsqldb'
18+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
19+
testImplementation 'org.springframework.batch:spring-batch-test'
3220
}
33-

0 commit comments

Comments
 (0)