diff --git a/spring-batch-samples/README.md b/spring-batch-samples/README.md index ec763cbcc..77eac1ef5 100644 --- a/spring-batch-samples/README.md +++ b/spring-batch-samples/README.md @@ -225,285 +225,10 @@ object ### Football Job -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/data/footballjob/input/. Each line within this -file represents a player, with a unique id, the player’s name, -position, etc: +This is a (American) Football statistics loading job. It loads two files containing players and games +data into a database, and then combines them to summarise how each player performed for a particular year. - 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: - -**Note:** One of the nice features of Spring is a project called -Spring IDE. When you download the project you can install Spring -IDE and add the Spring configurations to the IDE project. This is -not a tutorial on Spring IDE but the visual view into Spring beans -is helpful in understanding the structure of a Job -Configuration. Spring IDE produces the following diagram: - -![Spring Batch Football Object Model](src/site/resources/images/spring-batch-football-graph.jpg "Spring Batch Football Object Model") - -This corresponds exactly with the `footballJob.xml` job -configuration file which can be found in the jobs folder under -`src/main/resources`. When you drill down into the football job -you will see that the configuration has a list of steps: - - - - - - - - - -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: - - - - - - - - - - - - - - - - -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: - - - - - - - - - - - - - -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: - - 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: - - - - - - - - - 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 - - - - -The `JdbcCursorItemReader` has three dependences: - -* 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. Finally to run this -sample application you can execute the JUnit test -`FootballJobFunctionalTests`, and you'll see an output showing -each of the records as they are processed. Please keep in mind that -AoP is used to wrap the `ItemWriter` and output each record as it -is processed to the logger, which may impact performance. +[Football Job](./src/main/java/org/springframework/batch/sample/football/README.md) ### Header Footer Sample diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/FootballJobConfiguration.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/FootballJobConfiguration.java new file mode 100644 index 000000000..0b1369953 --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/FootballJobConfiguration.java @@ -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 playerFileItemReader() { + return new FlatFileItemReaderBuilder().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).chunk(2, transactionManager) + .reader(playerFileItemReader()) + .writer(playerWriter()) + .build(); + } + + // step 2 configuration + + @Bean + public FlatFileItemReader gameFileItemReader() { + return new FlatFileItemReaderBuilder().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).chunk(2, transactionManager) + .reader(gameFileItemReader()) + .writer(gameWriter()) + .build(); + } + + // step 3 configuration + + @Bean + public JdbcCursorItemReader 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().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) + .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); + } + +} \ No newline at end of file diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/Game.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/Game.java similarity index 98% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/Game.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/Game.java index fc01d0d03..9f68bbe65 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/Game.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/Game.java @@ -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; diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/Player.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/Player.java similarity index 94% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/Player.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/Player.java index 92a2c6a7c..c89dc0c17 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/Player.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/Player.java @@ -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; diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/PlayerDao.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/PlayerDao.java similarity index 92% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/PlayerDao.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/PlayerDao.java index 4f72f8652..ff53ea975 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/PlayerDao.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/PlayerDao.java @@ -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. diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/PlayerSummary.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/PlayerSummary.java similarity index 98% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/PlayerSummary.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/PlayerSummary.java index e27775813..a2542e316 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/PlayerSummary.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/PlayerSummary.java @@ -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. diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/README.md b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/README.md new file mode 100644 index 000000000..905a3acdf --- /dev/null +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/README.md @@ -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 + + + + + + + +``` + +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 + + + + + + + + + + + + + + +``` + +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 + + + + + + + + + + + +``` + +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 + + + + + + + + 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 + + + +``` + +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 +``` \ No newline at end of file diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/FootballExceptionHandler.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/FootballExceptionHandler.java similarity index 94% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/FootballExceptionHandler.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/FootballExceptionHandler.java index 90f8861f0..5c96dab52 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/FootballExceptionHandler.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/FootballExceptionHandler.java @@ -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; diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/GameFieldSetMapper.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/GameFieldSetMapper.java similarity index 92% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/GameFieldSetMapper.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/GameFieldSetMapper.java index 4e0d44e24..2bb9287fb 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/GameFieldSetMapper.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/GameFieldSetMapper.java @@ -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 { diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcGameDao.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcGameDao.java similarity index 94% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcGameDao.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcGameDao.java index dad2229df..809fb3a56 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcGameDao.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcGameDao.java @@ -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; diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerDao.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcPlayerDao.java similarity index 88% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerDao.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcPlayerDao.java index ecc0260b5..63d3b94f1 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerDao.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcPlayerDao.java @@ -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; diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerSummaryDao.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcPlayerSummaryDao.java similarity index 94% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerSummaryDao.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcPlayerSummaryDao.java index 45b407bee..388348847 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerSummaryDao.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/JdbcPlayerSummaryDao.java @@ -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; diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerFieldSetMapper.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerFieldSetMapper.java similarity index 90% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerFieldSetMapper.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerFieldSetMapper.java index 8b9a545db..406135c29 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerFieldSetMapper.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerFieldSetMapper.java @@ -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 { diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerItemWriter.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerItemWriter.java similarity index 84% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerItemWriter.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerItemWriter.java index c3b2b99fe..2bdc5e151 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerItemWriter.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerItemWriter.java @@ -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 { diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerSummaryMapper.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerSummaryMapper.java similarity index 86% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerSummaryMapper.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerSummaryMapper.java index 1dc5b0078..9eb2557a8 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerSummaryMapper.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerSummaryMapper.java @@ -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 diff --git a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerSummaryRowMapper.java b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerSummaryRowMapper.java similarity index 86% rename from spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerSummaryRowMapper.java rename to spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerSummaryRowMapper.java index 33eb01cd4..61292c041 100644 --- a/spring-batch-samples/src/main/java/org/springframework/batch/sample/domain/football/internal/PlayerSummaryRowMapper.java +++ b/spring-batch-samples/src/main/java/org/springframework/batch/sample/football/internal/PlayerSummaryRowMapper.java @@ -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 diff --git a/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football-job-context.xml b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football-job-context.xml deleted file mode 100644 index 4939869e4..000000000 --- a/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football-job-context.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/spring-batch-samples/src/main/resources/data/footballjob/input/games-small.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/games-small.csv similarity index 100% rename from spring-batch-samples/src/main/resources/data/footballjob/input/games-small.csv rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/games-small.csv diff --git a/spring-batch-samples/src/main/resources/data/footballjob/input/games.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/games.csv similarity index 100% rename from spring-batch-samples/src/main/resources/data/footballjob/input/games.csv rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/games.csv diff --git a/spring-batch-samples/src/main/resources/data/footballjob/input/player-containsBadRecords.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player-containsBadRecords.csv similarity index 100% rename from spring-batch-samples/src/main/resources/data/footballjob/input/player-containsBadRecords.csv rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player-containsBadRecords.csv diff --git a/spring-batch-samples/src/main/resources/data/footballjob/input/player-small1.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player-small1.csv similarity index 100% rename from spring-batch-samples/src/main/resources/data/footballjob/input/player-small1.csv rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player-small1.csv diff --git a/spring-batch-samples/src/main/resources/data/footballjob/input/player-small2.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player-small2.csv similarity index 100% rename from spring-batch-samples/src/main/resources/data/footballjob/input/player-small2.csv rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player-small2.csv diff --git a/spring-batch-samples/src/main/resources/data/footballjob/input/player.csv b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player.csv similarity index 100% rename from spring-batch-samples/src/main/resources/data/footballjob/input/player.csv rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/data/player.csv diff --git a/spring-batch-samples/src/main/resources/jobs/footballJob.xml b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/job/footballJob.xml similarity index 65% rename from spring-batch-samples/src/main/resources/jobs/footballJob.xml rename to spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/job/footballJob.xml index 9cd3bab22..5a66eee45 100644 --- a/spring-batch-samples/src/main/resources/jobs/footballJob.xml +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/job/footballJob.xml @@ -1,13 +1,11 @@ - + @@ -29,24 +27,24 @@ - + - + - + - + - + @@ -55,14 +53,14 @@ - + - + @@ -71,7 +69,7 @@ - + @@ -80,7 +78,7 @@ - + @@ -95,25 +93,6 @@ - - - - - - - - - - - diff --git a/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/sql/schema.sql b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/sql/schema.sql new file mode 100644 index 000000000..4aab4d4ea --- /dev/null +++ b/spring-batch-samples/src/main/resources/org/springframework/batch/sample/football/sql/schema.sql @@ -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 +) ; \ No newline at end of file diff --git a/spring-batch-samples/src/site/resources/images/spring-batch-football-graph.jpg b/spring-batch-samples/src/site/resources/images/spring-batch-football-graph.jpg deleted file mode 100644 index d8898c135..000000000 Binary files a/spring-batch-samples/src/site/resources/images/spring-batch-football-graph.jpg and /dev/null differ diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/FootballJobFunctionalTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/FootballJobFunctionalTests.java similarity index 56% rename from spring-batch-samples/src/test/java/org/springframework/batch/sample/FootballJobFunctionalTests.java rename to spring-batch-samples/src/test/java/org/springframework/batch/sample/football/FootballJobFunctionalTests.java index d0a1a4107..628b61743 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/FootballJobFunctionalTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/FootballJobFunctionalTests.java @@ -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); + } + } diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcGameDaoIntegrationTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcGameDaoIntegrationTests.java similarity index 96% rename from spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcGameDaoIntegrationTests.java rename to spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcGameDaoIntegrationTests.java index ef78fecd1..9329a5859 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcGameDaoIntegrationTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcGameDaoIntegrationTests.java @@ -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; diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerDaoIntegrationTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcPlayerDaoIntegrationTests.java similarity index 94% rename from spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerDaoIntegrationTests.java rename to spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcPlayerDaoIntegrationTests.java index 6f61b2e7b..f16cc7aeb 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerDaoIntegrationTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcPlayerDaoIntegrationTests.java @@ -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; diff --git a/spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerSummaryDaoIntegrationTests.java b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcPlayerSummaryDaoIntegrationTests.java similarity index 94% rename from spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerSummaryDaoIntegrationTests.java rename to spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcPlayerSummaryDaoIntegrationTests.java index 732af3ab3..bb9177c74 100644 --- a/spring-batch-samples/src/test/java/org/springframework/batch/sample/domain/football/internal/JdbcPlayerSummaryDaoIntegrationTests.java +++ b/spring-batch-samples/src/test/java/org/springframework/batch/sample/football/internal/JdbcPlayerSummaryDaoIntegrationTests.java @@ -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;