@@ -0,0 +1,150 @@
|
||||
package org.springframework.batch.sample.football;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.Step;
|
||||
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
|
||||
import org.springframework.batch.core.job.builder.JobBuilder;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.step.builder.StepBuilder;
|
||||
import org.springframework.batch.item.database.JdbcCursorItemReader;
|
||||
import org.springframework.batch.item.database.builder.JdbcCursorItemReaderBuilder;
|
||||
import org.springframework.batch.item.file.FlatFileItemReader;
|
||||
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
|
||||
import org.springframework.batch.sample.football.internal.GameFieldSetMapper;
|
||||
import org.springframework.batch.sample.football.internal.JdbcGameDao;
|
||||
import org.springframework.batch.sample.football.internal.JdbcPlayerDao;
|
||||
import org.springframework.batch.sample.football.internal.JdbcPlayerSummaryDao;
|
||||
import org.springframework.batch.sample.football.internal.PlayerFieldSetMapper;
|
||||
import org.springframework.batch.sample.football.internal.PlayerItemWriter;
|
||||
import org.springframework.batch.sample.football.internal.PlayerSummaryMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
|
||||
import org.springframework.jdbc.support.JdbcTransactionManager;
|
||||
|
||||
@Configuration
|
||||
@EnableBatchProcessing
|
||||
public class FootballJobConfiguration {
|
||||
|
||||
// step 1 configuration
|
||||
|
||||
@Bean
|
||||
public FlatFileItemReader<Player> playerFileItemReader() {
|
||||
return new FlatFileItemReaderBuilder<Player>().name("playerFileItemReader")
|
||||
.resource(new ClassPathResource("org/springframework/batch/sample/football/data/player-small1.csv"))
|
||||
.delimited()
|
||||
.names("ID", "lastName", "firstName", "position", "birthYear", "debutYear")
|
||||
.fieldSetMapper(new PlayerFieldSetMapper())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlayerItemWriter playerWriter() {
|
||||
PlayerItemWriter playerItemWriter = new PlayerItemWriter();
|
||||
JdbcPlayerDao playerDao = new JdbcPlayerDao();
|
||||
playerDao.setDataSource(dataSource());
|
||||
playerItemWriter.setPlayerDao(playerDao);
|
||||
return playerItemWriter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step playerLoad(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
|
||||
return new StepBuilder("playerLoad", jobRepository).<Player, Player>chunk(2, transactionManager)
|
||||
.reader(playerFileItemReader())
|
||||
.writer(playerWriter())
|
||||
.build();
|
||||
}
|
||||
|
||||
// step 2 configuration
|
||||
|
||||
@Bean
|
||||
public FlatFileItemReader<Game> gameFileItemReader() {
|
||||
return new FlatFileItemReaderBuilder<Game>().name("gameFileItemReader")
|
||||
.resource(new ClassPathResource("org/springframework/batch/sample/football/data/games-small.csv"))
|
||||
.delimited()
|
||||
.names("id", "year", "team", "week", "opponent", "completes", "attempts", "passingYards", "passingTd",
|
||||
"interceptions", "rushes", "rushYards", "receptions", "receptionYards", "totalTd")
|
||||
.fieldSetMapper(new GameFieldSetMapper())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcGameDao gameWriter() {
|
||||
JdbcGameDao jdbcGameDao = new JdbcGameDao();
|
||||
jdbcGameDao.setDataSource(dataSource());
|
||||
return jdbcGameDao;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step gameLoad(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
|
||||
return new StepBuilder("gameLoad", jobRepository).<Game, Game>chunk(2, transactionManager)
|
||||
.reader(gameFileItemReader())
|
||||
.writer(gameWriter())
|
||||
.build();
|
||||
}
|
||||
|
||||
// step 3 configuration
|
||||
|
||||
@Bean
|
||||
public JdbcCursorItemReader<PlayerSummary> playerSummarizationSource() {
|
||||
String sql = """
|
||||
SELECT GAMES.player_id, GAMES.year_no, SUM(COMPLETES),
|
||||
SUM(ATTEMPTS), SUM(PASSING_YARDS), SUM(PASSING_TD),
|
||||
SUM(INTERCEPTIONS), SUM(RUSHES), SUM(RUSH_YARDS),
|
||||
SUM(RECEPTIONS), SUM(RECEPTIONS_YARDS), SUM(TOTAL_TD)
|
||||
from GAMES, PLAYERS where PLAYERS.player_id =
|
||||
GAMES.player_id group by GAMES.player_id, GAMES.year_no
|
||||
""";
|
||||
return new JdbcCursorItemReaderBuilder<PlayerSummary>().name("playerSummarizationSource")
|
||||
.ignoreWarnings(true)
|
||||
.sql(sql)
|
||||
.dataSource(dataSource())
|
||||
.rowMapper(new PlayerSummaryMapper())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcPlayerSummaryDao summaryWriter() {
|
||||
JdbcPlayerSummaryDao jdbcPlayerSummaryDao = new JdbcPlayerSummaryDao();
|
||||
jdbcPlayerSummaryDao.setDataSource(dataSource());
|
||||
return jdbcPlayerSummaryDao;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Step summarizationStep(JobRepository jobRepository, JdbcTransactionManager transactionManager) {
|
||||
return new StepBuilder("summarizationStep", jobRepository)
|
||||
.<PlayerSummary, PlayerSummary>chunk(2, transactionManager)
|
||||
.reader(playerSummarizationSource())
|
||||
.writer(summaryWriter())
|
||||
.build();
|
||||
}
|
||||
|
||||
// job configuration
|
||||
|
||||
@Bean
|
||||
public Job job(JobRepository jobRepository, Step playerLoad, Step gameLoad, Step summarizationStep) {
|
||||
return new JobBuilder("footballJob", jobRepository).start(playerLoad)
|
||||
.next(gameLoad)
|
||||
.next(summarizationStep)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL)
|
||||
.addScript("/org/springframework/batch/core/schema-drop-hsqldb.sql")
|
||||
.addScript("/org/springframework/batch/core/schema-hsqldb.sql")
|
||||
.addScript("/org/springframework/batch/sample/football/sql/schema.sql")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JdbcTransactionManager transactionManager(DataSource dataSource) {
|
||||
return new JdbcTransactionManager(dataSource);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football;
|
||||
package org.springframework.batch.sample.football;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
* Copyright 2006-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football;
|
||||
package org.springframework.batch.sample.football;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football;
|
||||
package org.springframework.batch.sample.football;
|
||||
|
||||
/**
|
||||
* Interface for writing {@link Player} objects to arbitrary output.
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.domain.football;
|
||||
package org.springframework.batch.sample.football;
|
||||
|
||||
/**
|
||||
* Domain object representing the summary of a given Player's year.
|
||||
@@ -0,0 +1,301 @@
|
||||
# Football Job
|
||||
|
||||
## About the sample
|
||||
|
||||
This is a (American) Football statistics loading job. We gave it the
|
||||
id of `footballJob` in our configuration file. Before diving
|
||||
into the batch job, we'll examine the two input files that need to
|
||||
be loaded. First is `player.csv`, which can be found in the
|
||||
samples project under `src/main/resources/org/springframework/batch/sample/football/data`.
|
||||
Each line within this file represents a player, with a unique id, the player’s name, position, etc:
|
||||
|
||||
```
|
||||
AbduKa00,Abdul-Jabbar,Karim,rb,1974,1996
|
||||
AbduRa00,Abdullah,Rabih,rb,1975,1999
|
||||
AberWa00,Abercrombie,Walter,rb,1959,1982
|
||||
AbraDa00,Abramowicz,Danny,wr,1945,1967
|
||||
AdamBo00,Adams,Bob,te,1946,1969
|
||||
AdamCh00,Adams,Charlie,wr,1979,2003
|
||||
...
|
||||
```
|
||||
|
||||
One of the first noticeable characteristics of the file is that each
|
||||
data element is separated by a comma, a format most are familiar
|
||||
with known as 'CSV'. Other separators such as pipes or semicolons
|
||||
could just as easily be used to delineate between unique
|
||||
elements. In general, it falls into one of two types of flat file
|
||||
formats: delimited or fixed length. (The fixed length case was
|
||||
covered in the `fixedLengthImportJob`).
|
||||
|
||||
The second file, 'games.csv' is formatted the same as the previous
|
||||
example, and resides in the same directory:
|
||||
|
||||
```
|
||||
AbduKa00,1996,mia,10,nwe,0,0,0,0,0,29,104,,16,2
|
||||
AbduKa00,1996,mia,11,clt,0,0,0,0,0,18,70,,11,2
|
||||
AbduKa00,1996,mia,12,oti,0,0,0,0,0,18,59,,0,0
|
||||
AbduKa00,1996,mia,13,pit,0,0,0,0,0,16,57,,0,0
|
||||
AbduKa00,1996,mia,14,rai,0,0,0,0,0,18,39,,7,0
|
||||
AbduKa00,1996,mia,15,nyg,0,0,0,0,0,17,96,,14,0
|
||||
...
|
||||
```
|
||||
|
||||
Each line in the file represents an individual player's performance
|
||||
in a particular game, containing such statistics as passing yards,
|
||||
receptions, rushes, and total touchdowns.
|
||||
|
||||
Our example batch job is going to load both files into a database,
|
||||
and then combine each to summarise how each player performed for a
|
||||
particular year. Although this example is fairly trivial, it shows
|
||||
multiple types of input, and the general style is a common batch
|
||||
scenario. That is, summarising a very large dataset so that it can
|
||||
be more easily manipulated or viewed by an online web-based
|
||||
application. In an enterprise solution the third step, the reporting
|
||||
step, could be implemented through the use of Eclipse BIRT or one of
|
||||
the many Java Reporting Engines. Given this description, we can then
|
||||
easily divide our batch job up into 3 'steps': one to load the
|
||||
player data, one to load the game data, and one to produce a summary
|
||||
report:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A(playerLoad) --> B(gameLoad)
|
||||
B --> C(playerSummarization)
|
||||
```
|
||||
|
||||
This corresponds exactly with the `footballJob.xml` job configuration file which can be found in
|
||||
`src/main/resources/org/springframework/batch/sample/football/job`.
|
||||
When you drill down into the football job you will see that the configuration has a list of steps:
|
||||
|
||||
```xml
|
||||
<property name="steps">
|
||||
<list>
|
||||
<bean id="playerload" parent="simpleStep" .../>
|
||||
<bean id="gameLoad" parent="simpleStep" .../>
|
||||
<bean id="playerSummarization" parent="simpleStep" .../>
|
||||
</list>
|
||||
</property>
|
||||
```
|
||||
|
||||
A step is run until there is no more input to process, which in
|
||||
this case would mean that each file has been completely
|
||||
processed. To describe it in a more narrative form: the first step,
|
||||
playerLoad, begins executing by grabbing one line of input from the
|
||||
file, and parsing it into a domain object. That domain object is
|
||||
then passed to a dao, which writes it out to the PLAYERS table. This
|
||||
action is repeated until there are no more lines in the file,
|
||||
causing the playerLoad step to finish. Next, the gameLoad step does
|
||||
the same for the games input file, inserting into the GAMES
|
||||
table. Once finished, the playerSummarization step can begin. Unlike
|
||||
the first two steps, playerSummarization input comes from the
|
||||
database, using a Sql statement to combine the GAMES and PLAYERS
|
||||
table. Each returned row is packaged into a domain object and
|
||||
written out to the PLAYER_SUMMARY table.
|
||||
|
||||
Now that we've discussed the entire flow of the batch job, we can
|
||||
dive deeper into the first step: playerLoad:
|
||||
|
||||
```xml
|
||||
<bean id="playerload" parent="simpleStep">
|
||||
<property name="commitInterval" value="${job.commit.interval}" />
|
||||
<property name="startLimit" value="100" />
|
||||
<property name="itemReader"
|
||||
ref="playerFileItemReader" />
|
||||
<property name="itemWriter">
|
||||
<bean
|
||||
class="org.springframework.batch.sample.football.internal.internal.PlayerItemWriter">
|
||||
<property name="playerDao">
|
||||
<bean
|
||||
class="org.springframework.batch.sample.football.internal.internal.JdbcPlayerDao">
|
||||
<property name="dataSource"
|
||||
ref="dataSource" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
```
|
||||
|
||||
The root bean in this case is a `SimpleStepFactoryBean`, which
|
||||
can be considered a 'blueprint' of sorts that tells the execution
|
||||
environment basic details about how the batch job should be
|
||||
executed. It contains four properties: (others have been removed for
|
||||
greater clarity) commitInterval, startLimit, itemReader and
|
||||
itemWriter . After performing all necessary startup, the framework
|
||||
will periodically delegate to the reader and writer. In this way,
|
||||
the developer can remain solely concerned with their business
|
||||
logic.
|
||||
|
||||
* *ItemReader* – the item reader is the source of the information
|
||||
pipe. At the most basic level input is read in from an input
|
||||
source, parsed into a domain object and returned. In this way, the
|
||||
good batch architecture practice of ensuring all data has been
|
||||
read before beginning processing can be enforced, along with
|
||||
providing a possible avenue for reuse.
|
||||
|
||||
* *ItemWriter* – this is the business logic. At a high level,
|
||||
the item writer takes the item returned from the reader
|
||||
and 'processes' it. In our case it's a data access object that is
|
||||
simply responsible for inserting a record into the PLAYERS
|
||||
table. As you can see the developer does very little.
|
||||
|
||||
The application developer simply provides a job configuration with a
|
||||
configured number of steps, an ItemReader associated to some type
|
||||
of input source, and ItemWriter associated to some type of
|
||||
output source and a little mapping of data from flat records to
|
||||
objects and the pipe is ready wired for processing.
|
||||
|
||||
Another property in the step configuration, the commitInterval,
|
||||
gives the framework vital information about how to control
|
||||
transactions during the batch run. Due to the large amount of data
|
||||
involved in batch processing, it is often advantageous to 'batch'
|
||||
together multiple logical units of work into one transaction, since
|
||||
starting and committing a transaction is extremely expensive. For
|
||||
example, in the playerLoad step, the framework calls read() on the
|
||||
item reader. The item reader reads one record from the file, and
|
||||
returns a domain object representation which is passed to the
|
||||
processor. The writer then writes the one record to the database. It
|
||||
can then be said that one iteration = one call to
|
||||
`ItemReader.read()` = one line of the file. Therefore, setting
|
||||
your commitInterval to 5 would result in the framework committing a
|
||||
transaction after 5 lines have been read from the file, with 5
|
||||
resultant entries in the PLAYERS table.
|
||||
|
||||
Following the general flow of the batch job, the next step is to
|
||||
describe how each line of the file will be parsed from its string
|
||||
representation into a domain object. The first thing the provider
|
||||
will need is an `ItemReader`, which is provided as part of the Spring
|
||||
Batch infrastructure. Because the input is flat-file based, a
|
||||
`FlatFileItemReader` is used:
|
||||
|
||||
```xml
|
||||
<bean id="playerFileItemReader"
|
||||
class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource"
|
||||
value="classpath:data/footballjob/input/${player.file.name}" />
|
||||
<property name="lineTokenizer">
|
||||
<bean
|
||||
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
|
||||
<property name="names"
|
||||
value="ID,lastName,firstName,position,birthYear,debutYear" />
|
||||
</bean>
|
||||
</property>
|
||||
<property name="fieldSetMapper">
|
||||
<bean
|
||||
class="org.springframework.batch.sample.football.internal.internal.PlayerFieldSetMapper" />
|
||||
</property>
|
||||
</bean>
|
||||
```
|
||||
|
||||
There are three required dependencies of the item reader; the first
|
||||
is a resource to read in, which is the file to process. The second
|
||||
dependency is a `LineTokenizer`. The interface for a
|
||||
`LineTokenizer` is very simple, given a string; it will return a
|
||||
`FieldSet` that wraps the results from splitting the provided
|
||||
string. A `FieldSet` is Spring Batch's abstraction for flat file
|
||||
data. It allows developers to work with file input in much the same
|
||||
way as they would work with database input. All the developers need
|
||||
to provide is a `FieldSetMapper` (similar to a Spring
|
||||
`RowMapper`) that will map the provided `FieldSet` into an
|
||||
`Object`. Simply by providing the names of each token to the
|
||||
`LineTokenizer`, the `ItemReader` can pass the
|
||||
`FieldSet` into our `PlayerMapper`, which implements the
|
||||
`FieldSetMapper` interface. There is a single method,
|
||||
`mapLine()`, which maps `FieldSet`s the same way that
|
||||
developers are comfortable mapping `ResultSet`s into Java
|
||||
`Object`s, either by index or field name. This behaviour is by
|
||||
intention and design similar to the `RowMapper` passed into a
|
||||
`JdbcTemplate`. You can see this below:
|
||||
|
||||
```java
|
||||
public class PlayerMapper implements FieldSetMapper {
|
||||
|
||||
public Object mapLine(FieldSet fs) {
|
||||
|
||||
if(fs == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
Player player = new Player();
|
||||
player.setID(fs.readString("ID"));
|
||||
player.setLastName(fs.readString("lastName"));
|
||||
player.setFirstName(fs.readString("firstName"));
|
||||
player.setPosition(fs.readString("position"));
|
||||
player.setDebutYear(fs.readInt("debutYear"));
|
||||
player.setBirthYear(fs.readInt("birthYear"));
|
||||
|
||||
return player;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The flow of the `ItemReader`, in this case, starts with a call
|
||||
to read the next line from the file. This is passed into the
|
||||
provided `LineTokenizer`. The `LineTokenizer` splits the
|
||||
line at every comma, and creates a `FieldSet` using the created
|
||||
`String` array and the array of names passed in.
|
||||
|
||||
**Note:** it is only necessary to provide the names to create the
|
||||
`FieldSet` if you wish to access the field by name, rather
|
||||
than by index.
|
||||
|
||||
Once the domain representation of the data has been returned by the
|
||||
provider, (i.e. a `Player` object in this case) it is passed to
|
||||
the `ItemWriter`, which is essentially a Dao that uses a Spring
|
||||
`JdbcTemplate` to insert a new row in the PLAYERS table.
|
||||
|
||||
The next step, gameLoad, works almost exactly the same as the
|
||||
playerLoad step, except the games file is used.
|
||||
|
||||
The final step, playerSummarization, is much like the previous two
|
||||
steps, in that it reads from a reader and returns a domain object to
|
||||
a writer. However, in this case, the input source is the database,
|
||||
not a file:
|
||||
|
||||
```xml
|
||||
<bean id="playerSummarizationSource" class="org.springframework.batch.item.database.JdbcCursorItemReader">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
<property name="mapper">
|
||||
<bean
|
||||
class="org.springframework.batch.sample.football.internal.internal.PlayerSummaryMapper" />
|
||||
</property>
|
||||
<property name="sql">
|
||||
<value>
|
||||
SELECT games.player_id, games.year_no, SUM(COMPLETES),
|
||||
SUM(ATTEMPTS), SUM(PASSING_YARDS), SUM(PASSING_TD),
|
||||
SUM(INTERCEPTIONS), SUM(RUSHES), SUM(RUSH_YARDS),
|
||||
SUM(RECEPTIONS), SUM(RECEPTIONS_YARDS), SUM(TOTAL_TD)
|
||||
from games, players where players.player_id =
|
||||
games.player_id group by games.player_id, games.year_no
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
```
|
||||
|
||||
The `JdbcCursorItemReader` has three dependencies:
|
||||
|
||||
* A `DataSource`
|
||||
* The `RowMapper` to use for each row.
|
||||
* The Sql statement used to create the cursor.
|
||||
|
||||
When the step is first started, a query will be run against the
|
||||
database to open a cursor, and each call to `itemReader.read()`
|
||||
will move the cursor to the next row, using the provided
|
||||
`RowMapper` to return the correct object. As with the previous
|
||||
two steps, each record returned by the provider will be written out
|
||||
to the database in the PLAYER_SUMMARY table.
|
||||
|
||||
The equivalent Java configuration of the football job can be found in
|
||||
`org/springframework/batch/sample/football/FootballJobConfiguration.java`.
|
||||
|
||||
## Run the sample
|
||||
|
||||
You can run the sample from the command line as following:
|
||||
|
||||
```
|
||||
$>cd spring-batch-samples
|
||||
# Launch the sample using the XML configuration
|
||||
$>../mvnw -Dtest=FootballJobFunctionalTests#testLaunchJobWithXmlConfiguration test
|
||||
# Launch the sample using the Java configuration
|
||||
$>../mvnw -Dtest=FootballJobFunctionalTests#testLaunchJobWithJavaConfiguration test
|
||||
```
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
import org.springframework.batch.sample.domain.football.Game;
|
||||
import org.springframework.batch.sample.football.Game;
|
||||
|
||||
public class GameFieldSetMapper implements FieldSetMapper<Game> {
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import org.springframework.batch.item.Chunk;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.sample.domain.football.Game;
|
||||
import org.springframework.batch.sample.football.Game;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
|
||||
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.sample.domain.football.Player;
|
||||
import org.springframework.batch.sample.domain.football.PlayerDao;
|
||||
import org.springframework.batch.sample.football.Player;
|
||||
import org.springframework.batch.sample.football.PlayerDao;
|
||||
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
@@ -14,13 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.item.Chunk;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.sample.domain.football.PlayerSummary;
|
||||
import org.springframework.batch.sample.football.PlayerSummary;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
@@ -14,11 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import org.springframework.batch.item.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.item.file.transform.FieldSet;
|
||||
import org.springframework.batch.sample.domain.football.Player;
|
||||
import org.springframework.batch.sample.football.Player;
|
||||
|
||||
public class PlayerFieldSetMapper implements FieldSetMapper<Player> {
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import org.springframework.batch.item.Chunk;
|
||||
import org.springframework.batch.item.ItemWriter;
|
||||
import org.springframework.batch.sample.domain.football.Player;
|
||||
import org.springframework.batch.sample.domain.football.PlayerDao;
|
||||
import org.springframework.batch.sample.football.Player;
|
||||
import org.springframework.batch.sample.football.PlayerDao;
|
||||
|
||||
public class PlayerItemWriter implements ItemWriter<Player> {
|
||||
|
||||
@@ -13,17 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.batch.sample.domain.football.PlayerSummary;
|
||||
import org.springframework.batch.sample.football.PlayerSummary;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* RowMapper used to map a ResultSet to a
|
||||
* {@link org.springframework.batch.sample.domain.football.PlayerSummary}
|
||||
* RowMapper used to map a ResultSet to a {@link PlayerSummary}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Mahmoud Ben Hassine
|
||||
@@ -13,17 +13,16 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.batch.sample.domain.football.PlayerSummary;
|
||||
import org.springframework.batch.sample.football.PlayerSummary;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* RowMapper used to map a ResultSet to a
|
||||
* {@link org.springframework.batch.sample.domain.football.PlayerSummary}
|
||||
* RowMapper used to map a ResultSet to a {@link PlayerSummary}
|
||||
*
|
||||
* @author Lucas Ward
|
||||
* @author Mahmoud Ben Hassine
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<!-- useful for launching this job on its own from the command line -->
|
||||
<import resource="classpath:/simple-job-launcher-context.xml" />
|
||||
<import resource="classpath:/jobs/footballJob.xml" />
|
||||
</beans>
|
||||
|
Can't render this file because it is too large.
|
@@ -1,13 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
|
||||
http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/batch https://www.springframework.org/schema/batch/spring-batch.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<job id="footballJob" xmlns="http://www.springframework.org/schema/batch">
|
||||
<step id="playerload" next="gameLoad">
|
||||
<step id="playerLoad" next="gameLoad">
|
||||
<tasklet>
|
||||
<chunk reader="playerFileItemReader" writer="playerWriter"
|
||||
commit-interval="${job.commit.interval}" />
|
||||
@@ -29,24 +27,24 @@
|
||||
</tasklet>
|
||||
</step>
|
||||
|
||||
<bean id="playerWriter" class="org.springframework.batch.sample.domain.football.internal.PlayerItemWriter">
|
||||
<bean id="playerWriter" class="org.springframework.batch.sample.football.internal.PlayerItemWriter">
|
||||
<property name="playerDao">
|
||||
<bean class="org.springframework.batch.sample.domain.football.internal.JdbcPlayerDao">
|
||||
<bean class="org.springframework.batch.sample.football.internal.JdbcPlayerDao">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="gameWriter" class="org.springframework.batch.sample.domain.football.internal.JdbcGameDao">
|
||||
<bean id="gameWriter" class="org.springframework.batch.sample.football.internal.JdbcGameDao">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="summaryWriter" class="org.springframework.batch.sample.domain.football.internal.JdbcPlayerSummaryDao">
|
||||
<bean id="summaryWriter" class="org.springframework.batch.sample.football.internal.JdbcPlayerSummaryDao">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
</bean>
|
||||
|
||||
<bean id="playerFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="classpath:data/footballjob/input/${player.file.name}" />
|
||||
<property name="resource" value="classpath:org/springframework/batch/sample/football/data/${player.file.name}" />
|
||||
<property name="lineMapper">
|
||||
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
|
||||
<property name="lineTokenizer">
|
||||
@@ -55,14 +53,14 @@
|
||||
</bean>
|
||||
</property>
|
||||
<property name="fieldSetMapper">
|
||||
<bean class="org.springframework.batch.sample.domain.football.internal.PlayerFieldSetMapper" />
|
||||
<bean class="org.springframework.batch.sample.football.internal.PlayerFieldSetMapper" />
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="gameFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
|
||||
<property name="resource" value="classpath:data/footballjob/input/${games.file.name}" />
|
||||
<property name="resource" value="classpath:org/springframework/batch/sample/football/data/${games.file.name}" />
|
||||
<property name="lineMapper">
|
||||
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
|
||||
<property name="lineTokenizer">
|
||||
@@ -71,7 +69,7 @@
|
||||
</bean>
|
||||
</property>
|
||||
<property name="fieldSetMapper">
|
||||
<bean class="org.springframework.batch.sample.domain.football.internal.GameFieldSetMapper" />
|
||||
<bean class="org.springframework.batch.sample.football.internal.GameFieldSetMapper" />
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
@@ -80,7 +78,7 @@
|
||||
<bean id="playerSummarizationSource" class="org.springframework.batch.item.database.JdbcCursorItemReader">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
<property name="rowMapper">
|
||||
<bean class="org.springframework.batch.sample.domain.football.internal.PlayerSummaryMapper" />
|
||||
<bean class="org.springframework.batch.sample.football.internal.PlayerSummaryMapper" />
|
||||
</property>
|
||||
<property name="sql">
|
||||
<value>
|
||||
@@ -95,25 +93,6 @@
|
||||
<property name="verifyCursorPosition" value="${batch.verify.cursor.position}"/>
|
||||
</bean>
|
||||
|
||||
<aop:config>
|
||||
<aop:aspect id="moduleLogging" ref="logAdvice">
|
||||
<aop:after
|
||||
pointcut="execution( * org.springframework.batch.item.ItemWriter+.write(..)) and args(item)"
|
||||
method="doStronglyTypedLogging" />
|
||||
</aop:aspect>
|
||||
<aop:aspect ref="eventAdvice">
|
||||
<aop:before
|
||||
pointcut="execution( * org.springframework.batch..Step+.execute(..)) and args(stepExecution)"
|
||||
method="before" />
|
||||
<aop:after
|
||||
pointcut="execution( * org.springframework.batch..Step+.execute(..)) and args(stepExecution)"
|
||||
method="after" />
|
||||
<aop:after-throwing throwing="t"
|
||||
pointcut="execution( * org.springframework.batch..Step+.execute(..)) and args(stepExecution)"
|
||||
method="onError" />
|
||||
</aop:aspect>
|
||||
</aop:config>
|
||||
|
||||
<bean id="footballProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
|
||||
<property name="properties">
|
||||
<value>
|
||||
@@ -0,0 +1,45 @@
|
||||
DROP TABLE PLAYERS IF EXISTS;
|
||||
DROP TABLE GAMES IF EXISTS;
|
||||
DROP TABLE PLAYER_SUMMARY IF EXISTS;
|
||||
|
||||
CREATE TABLE PLAYERS (
|
||||
PLAYER_ID CHAR(8) NOT NULL PRIMARY KEY,
|
||||
LAST_NAME VARCHAR(35) NOT NULL,
|
||||
FIRST_NAME VARCHAR(25) NOT NULL,
|
||||
POS VARCHAR(10) ,
|
||||
YEAR_OF_BIRTH BIGINT NOT NULL,
|
||||
YEAR_DRAFTED BIGINT NOT NULL
|
||||
) ;
|
||||
|
||||
CREATE TABLE GAMES (
|
||||
PLAYER_ID CHAR(8) NOT NULL,
|
||||
YEAR_NO BIGINT NOT NULL,
|
||||
TEAM CHAR(3) NOT NULL,
|
||||
WEEK BIGINT NOT NULL,
|
||||
OPPONENT CHAR(3) ,
|
||||
COMPLETES BIGINT ,
|
||||
ATTEMPTS BIGINT ,
|
||||
PASSING_YARDS BIGINT ,
|
||||
PASSING_TD BIGINT ,
|
||||
INTERCEPTIONS BIGINT ,
|
||||
RUSHES BIGINT ,
|
||||
RUSH_YARDS BIGINT ,
|
||||
RECEPTIONS BIGINT ,
|
||||
RECEPTIONS_YARDS BIGINT ,
|
||||
TOTAL_TD BIGINT
|
||||
) ;
|
||||
|
||||
CREATE TABLE PLAYER_SUMMARY (
|
||||
ID CHAR(8) NOT NULL,
|
||||
YEAR_NO BIGINT NOT NULL,
|
||||
COMPLETES BIGINT NOT NULL ,
|
||||
ATTEMPTS BIGINT NOT NULL ,
|
||||
PASSING_YARDS BIGINT NOT NULL ,
|
||||
PASSING_TD BIGINT NOT NULL ,
|
||||
INTERCEPTIONS BIGINT NOT NULL ,
|
||||
RUSHES BIGINT NOT NULL ,
|
||||
RUSH_YARDS BIGINT NOT NULL ,
|
||||
RECEPTIONS BIGINT NOT NULL ,
|
||||
RECEPTIONS_YARDS BIGINT NOT NULL ,
|
||||
TOTAL_TD BIGINT NOT NULL
|
||||
) ;
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2007-2022 the original author or authors.
|
||||
* Copyright 2007-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,22 +13,27 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample;
|
||||
package org.springframework.batch.sample.football;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.test.JobLauncherTestUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.test.jdbc.JdbcTestUtils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@SpringJUnitConfig(
|
||||
locations = { "/simple-job-launcher-context.xml", "/jobs/footballJob.xml", "/job-runner-context.xml" })
|
||||
@SpringJUnitConfig(locations = { "/simple-job-launcher-context.xml",
|
||||
"/org/springframework/batch/sample/football/job/footballJob.xml", "/job-runner-context.xml" })
|
||||
class FootballJobFunctionalTests {
|
||||
|
||||
@Autowired
|
||||
@@ -42,7 +47,7 @@ class FootballJobFunctionalTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLaunchJob() throws Exception {
|
||||
void testLaunchJobWithXmlConfiguration() throws Exception {
|
||||
JdbcTestUtils.deleteFromTables(jdbcTemplate, "PLAYERS", "GAMES", "PLAYER_SUMMARY");
|
||||
|
||||
jobLauncherTestUtils.launchJob();
|
||||
@@ -51,4 +56,20 @@ class FootballJobFunctionalTests {
|
||||
assertTrue(count > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLaunchJobWithJavaConfiguration() throws Exception {
|
||||
// given
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(FootballJobConfiguration.class);
|
||||
JobLauncher jobLauncher = context.getBean(JobLauncher.class);
|
||||
Job job = context.getBean(Job.class);
|
||||
|
||||
// when
|
||||
jobLauncher.run(job, new JobParameters());
|
||||
|
||||
// then
|
||||
int count = JdbcTestUtils.countRowsInTable(new JdbcTemplate(context.getBean(DataSource.class)),
|
||||
"PLAYER_SUMMARY");
|
||||
assertTrue(count > 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.batch.item.Chunk;
|
||||
import org.springframework.batch.sample.domain.football.Game;
|
||||
import org.springframework.batch.sample.football.Game;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
@@ -13,14 +13,14 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.batch.sample.domain.football.Player;
|
||||
import org.springframework.batch.sample.football.Player;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.batch.sample.domain.football.internal;
|
||||
package org.springframework.batch.sample.football.internal;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.batch.item.Chunk;
|
||||
import org.springframework.batch.sample.domain.football.PlayerSummary;
|
||||
import org.springframework.batch.sample.football.PlayerSummary;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
Reference in New Issue
Block a user