Skip to content

Commit 89c7100

Browse files
fmbenhassineFBibonne
authored andcommitted
Add sample job based on PetClinic application
Signed-off-by: Mahmoud Ben Hassine <[email protected]> Signed-off-by: Fabrice Bibonne <[email protected]>
1 parent 6eb2da7 commit 89c7100

File tree

7 files changed

+260
-0
lines changed

7 files changed

+260
-0
lines changed

spring-batch-samples/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ The IO Sample Job has a number of special instances that show different IO featu
6161
| [multiResource Sample](#multiresource-input-output-job) | x | | | | | | | x | | x | | x |
6262
| [XML Input Output Sample](#xml-input-output) | | | x | | | | | | | | | |
6363
| [MongoDB sample](#mongodb-sample) | | | | | x | | | | x | | | |
64+
| [PetClinic sample](#petclinic-sample) | | | | | x | x | | | | | | |
6465

6566
### Common Sample Source Structures
6667

@@ -615,6 +616,16 @@ $>docker run --name mongodb --rm -d -p 27017:27017 mongo
615616
Once MongoDB is up and running, run the `org.springframework.batch.samples.mongodb.MongoDBSampleApp`
616617
class without any argument to start the sample.
617618

619+
### PetClinic sample
620+
621+
This sample uses the [PetClinic Spring application](https://github.com/spring-projects/spring-petclinic) to show how to use
622+
Spring Batch to export data from a relational database table to a flat file.
623+
624+
The job in this sample is a single-step job that exports data from the `owners` table
625+
to a flat file named `owners.csv`.
626+
627+
[PetClinic Sample](src/main/java/org/springframework/batch/samples/petclinic/README.md)
628+
618629
### Adhoc Loop and JMX Sample
619630

620631
This job is simply an infinite loop. It runs forever so it is
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.samples.petclinic;
17+
18+
public record Owner(int id, String firstname, String lastname, String address, String city, String telephone) {
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.samples.petclinic;
17+
18+
import javax.sql.DataSource;
19+
20+
import org.springframework.batch.core.Job;
21+
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
22+
import org.springframework.batch.core.job.builder.JobBuilder;
23+
import org.springframework.batch.core.repository.JobRepository;
24+
import org.springframework.batch.core.step.builder.StepBuilder;
25+
import org.springframework.batch.item.database.JdbcCursorItemReader;
26+
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
27+
import org.springframework.batch.item.file.FlatFileItemWriter;
28+
import org.springframework.batch.item.file.builder.FlatFileItemWriterBuilder;
29+
import org.springframework.batch.samples.common.DataSourceConfiguration;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.context.annotation.Import;
33+
import org.springframework.core.io.FileSystemResource;
34+
import org.springframework.jdbc.core.DataClassRowMapper;
35+
import org.springframework.jdbc.support.JdbcTransactionManager;
36+
37+
@Configuration
38+
@EnableBatchProcessing
39+
@Import(DataSourceConfiguration.class)
40+
public class OwnersExportJobConfiguration {
41+
42+
@Bean
43+
public JdbcCursorItemReader<Owner> ownersReader(DataSource dataSource) {
44+
return new JdbcCursorItemReaderBuilder<Owner>().name("ownersReader")
45+
.sql("SELECT * FROM OWNERS")
46+
.dataSource(dataSource)
47+
.rowMapper(new DataClassRowMapper<>(Owner.class))
48+
.build();
49+
}
50+
51+
@Bean
52+
public FlatFileItemWriter<Owner> ownersWriter() {
53+
return new FlatFileItemWriterBuilder<Owner>().name("ownersWriter")
54+
.resource(new FileSystemResource("owners.csv"))
55+
.delimited()
56+
.names("id", "firstname", "lastname", "address", "city", "telephone")
57+
.build();
58+
}
59+
60+
@Bean
61+
public Job job(JobRepository jobRepository, JdbcTransactionManager transactionManager,
62+
JdbcCursorItemReader<Owner> ownersReader, FlatFileItemWriter<Owner> ownersWriter) {
63+
return new JobBuilder("ownersExportJob", jobRepository)
64+
.start(new StepBuilder("ownersExportStep", jobRepository).<Owner, Owner>chunk(5, transactionManager)
65+
.reader(ownersReader)
66+
.writer(ownersWriter)
67+
.build())
68+
.build();
69+
}
70+
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# PetClinic Job
2+
3+
## About the sample
4+
5+
This sample uses the [PetClinic Spring application](https://github.com/spring-projects/spring-petclinic) to show how to use
6+
Spring Batch to export data from a relational database table to a flat file.
7+
8+
The job in this sample is a single-step job that exports data from the `owners` table
9+
to a flat file named `owners.csv`.
10+
11+
## Run the sample
12+
13+
You can run the sample from the command line as following:
14+
15+
```
16+
$>cd spring-batch-samples
17+
# Launch the sample using the XML configuration
18+
$>../mvnw -Dtest=PetClinicJobFunctionalTests#testLaunchJobWithXmlConfiguration test
19+
# Launch the sample using the Java configuration
20+
$>../mvnw -Dtest=PetClinicJobFunctionalTests#testLaunchJobWithJavaConfiguration test
21+
```

spring-batch-samples/src/main/resources/org/springframework/batch/samples/common/business-schema-hsqldb.sql

+23
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,26 @@ CREATE TABLE ERROR_LOG (
100100
STEP_NAME CHAR(20) ,
101101
MESSAGE VARCHAR(300) NOT NULL
102102
) ;
103+
104+
-- PetClinic sample tables
105+
106+
CREATE TABLE OWNERS (
107+
ID INTEGER IDENTITY PRIMARY KEY,
108+
FIRSTNAME VARCHAR(30),
109+
LASTNAME VARCHAR(30),
110+
ADDRESS VARCHAR(255),
111+
CITY VARCHAR(80),
112+
TELEPHONE VARCHAR(20)
113+
);
114+
115+
INSERT INTO OWNERS VALUES (1, 'George', 'Franklin', '110 W. Liberty St.', 'Madison', '6085551023');
116+
INSERT INTO OWNERS VALUES (2, 'Betty', 'Davis', '638 Cardinal Ave.', 'Sun Prairie', '6085551749');
117+
INSERT INTO OWNERS VALUES (3, 'Eduardo', 'Rodriquez', '2693 Commerce St.', 'McFarland', '6085558763');
118+
INSERT INTO OWNERS VALUES (4, 'Harold', 'Davis', '563 Friendly St.', 'Windsor', '6085553198');
119+
INSERT INTO OWNERS VALUES (5, 'Peter', 'McTavish', '2387 S. Fair Way', 'Madison', '6085552765');
120+
INSERT INTO OWNERS VALUES (6, 'Jean', 'Coleman', '105 N. Lake St.', 'Monona', '6085552654');
121+
INSERT INTO OWNERS VALUES (7, 'Jeff', 'Black', '1450 Oak Blvd.', 'Monona', '6085555387');
122+
INSERT INTO OWNERS VALUES (8, 'Maria', 'Escobito', '345 Maple St.', 'Madison', '6085557683');
123+
INSERT INTO OWNERS VALUES (9, 'David', 'Schroeder', '2749 Blackhawk Trail', 'Madison', '6085559435');
124+
INSERT INTO OWNERS VALUES (10, 'Carlos', 'Estaban', '2335 Independence La.', 'Waunakee', '6085555487');
125+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:batch="http://www.springframework.org/schema/batch"
5+
xsi:schemaLocation="
6+
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd
7+
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
8+
9+
<batch:job id="ownersExportJob" xmlns="http://www.springframework.org/schema/batch">
10+
<batch:step id="ownersExportStep">
11+
<batch:tasklet>
12+
<batch:chunk reader="ownersReader" writer="ownersWriter" commit-interval="5"/>
13+
</batch:tasklet>
14+
</batch:step>
15+
</batch:job>
16+
17+
<bean id="ownersReader" class="org.springframework.batch.item.database.JdbcCursorItemReader">
18+
<property name="dataSource" ref="dataSource"/>
19+
<property name="sql" value="select * from owners"/>
20+
<property name="rowMapper">
21+
<bean class="org.springframework.jdbc.core.DataClassRowMapper">
22+
<constructor-arg value="org.springframework.batch.samples.petclinic.Owner"/>
23+
</bean>
24+
</property>
25+
</bean>
26+
27+
<bean id="ownersWriter" class="org.springframework.batch.item.file.FlatFileItemWriter">
28+
<property name="resource" value="file:owners.csv" />
29+
<property name="lineAggregator">
30+
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
31+
<property name="delimiter" value=","/>
32+
<property name="fieldExtractor">
33+
<bean class="org.springframework.batch.item.file.transform.RecordFieldExtractor">
34+
<constructor-arg value="org.springframework.batch.samples.petclinic.Owner"/>
35+
<property name="names" value="id,firstname,lastname,address,city,telephone"/>
36+
</bean>
37+
</property>
38+
</bean>
39+
</property>
40+
</bean>
41+
42+
</beans>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.batch.samples.petclinic;
17+
18+
import java.io.IOException;
19+
import java.nio.file.Files;
20+
import java.nio.file.Paths;
21+
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
25+
import org.springframework.batch.core.BatchStatus;
26+
import org.springframework.batch.core.Job;
27+
import org.springframework.batch.core.JobExecution;
28+
import org.springframework.batch.core.JobParameters;
29+
import org.springframework.batch.core.launch.JobLauncher;
30+
import org.springframework.batch.test.JobLauncherTestUtils;
31+
import org.springframework.beans.factory.annotation.Autowired;
32+
import org.springframework.context.ApplicationContext;
33+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
34+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
35+
36+
import static org.junit.jupiter.api.Assertions.assertEquals;
37+
38+
@SpringJUnitConfig(locations = { "/simple-job-launcher-context.xml",
39+
"/org/springframework/batch/samples/petclinic/job/ownersExportJob.xml" })
40+
class PetClinicJobFunctionalTests {
41+
42+
@Autowired
43+
private JobLauncherTestUtils jobLauncherTestUtils;
44+
45+
@BeforeEach
46+
public void setup() throws IOException {
47+
Files.deleteIfExists(Paths.get("owners.csv"));
48+
}
49+
50+
@Test
51+
void testLaunchJobWithXmlConfiguration() throws Exception {
52+
// when
53+
JobExecution jobExecution = jobLauncherTestUtils.launchJob();
54+
55+
// then
56+
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
57+
}
58+
59+
@Test
60+
void testLaunchJobWithJavaConfiguration() throws Exception {
61+
// given
62+
ApplicationContext context = new AnnotationConfigApplicationContext(OwnersExportJobConfiguration.class);
63+
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
64+
Job job = context.getBean(Job.class);
65+
66+
// when
67+
JobExecution jobExecution = jobLauncher.run(job, new JobParameters());
68+
69+
// then
70+
assertEquals(BatchStatus.COMPLETED, jobExecution.getStatus());
71+
}
72+
73+
}

0 commit comments

Comments
 (0)