diff --git a/docs/src/site/docbook/reference/readersAndWriters.xml b/docs/src/site/docbook/reference/readersAndWriters.xml
index d15854e06..44c61c650 100644
--- a/docs/src/site/docbook/reference/readersAndWriters.xml
+++ b/docs/src/site/docbook/reference/readersAndWriters.xml
@@ -50,7 +50,7 @@
ItemReader is a basic interface for generic
input operations:
- public interface ItemReader {
+
+]]>
The read method defines the most essential
contract of the ItemReader, calling it returns one
@@ -97,7 +97,7 @@
As with ItemReader,
ItemWriter is a fairly generic interface:
- public interface ItemWriter {
+
+]]>
As with read on
ItemReader, write provides
@@ -135,7 +135,7 @@
readers and writers need to be opened, closed, and require a mechanism for
persisting state:
- public interface ItemStream {
+
+]]>
Before describing each method, its worth briefly mentioning the
ExecutionContext. Clients of an
@@ -203,11 +203,11 @@
fields so that the fields may be accessed either by index or name as
patterned after ResultSet:
- String[] tokens = new String[]{"foo", "1", "true"};
+
+ boolean booleanValue = fs.readBoolean(2);]]>
There are many more options on the FieldSet
interface, such as Date, long,
@@ -240,9 +240,9 @@
Framework, Chapter 4.Resources. Therefore, this
guide will not go into the details of creating
Resource objects. However, a simple example of a
- file system resource can be found below:
+ file system resource can be found below:
+ ]]>
In complex batch environments the directory structures are often
managed by the EAI infrastructure where drop zones for external
@@ -336,11 +336,11 @@
LineTokenizer to translate a line of data from
a resource into an object of the desired type:
- public interface FieldSetMapper {
+
+ }]]>
The pattern used is the same as RowMapper
used by JdbcTemplate.
@@ -356,11 +356,11 @@
FieldSet is necessary. In Spring Batch, this is
called a LineTokenizer:
- public interface LineTokenizer {
+
+ }]]>
The contract of a LineTokenizer is such
that, given a line of input (in theory the
@@ -420,14 +420,14 @@
In code, the above flow looks like the following:
- String line = readLine();
+
+ return null;]]>
Exception handling has been removed for clarity.
@@ -435,16 +435,16 @@
The following example will be used to illustrate this using an
actual domain scenario. This particular batch job reads in football
- players from the following file: ID,lastName,firstName,position,birthYear,debutYear
+ players from the following file:
+ "AdamCh00,Adams,Charlie,wr,1979,2003" ]]>
The contents of this file will be mapped to the following Player
- domain object:
+ domain object:
+ ]]>
In order to map a FieldSet into a Player
object, a FieldSetMapper that returns players
needs to be defined:
-
+
+ } ]]>
The file can then be read by correctly constructing a
FlatFileItemReader and calling
read:
-
+
+]]>
Each call to read will return a new
Player object from each line in the file. When the end of the file is
@@ -517,9 +517,9 @@
fields in the flat file are injected into the
LineTokenizer:
-
+
+ ]]>
a FieldSetMapper can this use this
information as follows:
@@ -559,14 +559,14 @@
FieldSetMapper configuration looks like the
following:
- <bean id="fieldSetMapper"
- class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
- <property name="prototypeBeanName" value="player" />
- </bean>
+
+
+
- <bean id="player"
+
+ scope="prototype" />]]>
For each entry in the FieldSet, the
mapper will look for a corresponding setter on a new instance of the
@@ -586,11 +586,11 @@
organizations that use flat files use fixed length formats. An example
fixed length file is below:
- UK21341EAH4121131.11customer1
+
+ UK21341EAH4521535.11customer5]]>
While this looks like one large field, it actually represent 4
distinct fields:
@@ -620,37 +620,37 @@
FixedLengthLineTokenizer, each of these lengths
must be provided in the form of ranges:
-
- <bean id="fixedLengthLineTokenizer"
- class="org.springframework.batch.io.file.transform.FixedLengthTokenizer">
- <property name="names" value="ISIN, Quantity, Price, Customer" />
- <property name="columns" value="1-12, 13-15, 16-20, 21-29" />
- </bean>
+
+
+
+
-
+]]>
This LineTokenizer will return the same
FieldSet as if a dlimiter had been used,
allowing the same approachs above to be used such as the
BeanWrapperFieldSetMapper, in a way that is
- ignorant of how the actual line was parsed.
+ ignorant of how the actual line was parsed.
It should be noted that supporting the above ranges requires a
specialized property editor be configured anywhere in the
ApplicationContext:
-
- <bean id="customEditorConfigurer" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
- <property name="customEditors">
- <map>
- <entry key="org.springframework.batch.item.file.transform.Range[]">
- <bean class="org.springframework.batch.item.file.transform.RangeArrayPropertyEditor" />
- </entry>
- </map>
- </property>
- </bean>
+
+
+
+
+
-
+]]>
@@ -662,7 +662,7 @@
might have records spanning multiple lines with multiple formats. The
following excerpt from a file illustrates this:
- HEA;0013100345;2007-02-15
+
+ FOT;2;2;267.34]]>
Everything between the line starting with 'HEA' and the line
starting with 'FOT' is considered one record. The
PrefixMatchingCompositeLineTokenizer makes this easier by matching the
prefix in a line with a particular tokenizer:
- <bean id="orderFileDescriptor"
- class="org.springframework.batch.io.file.transform.PrefixMatchingCompositeLineTokenizer">
- <property name="tokenizers">
- <map>
- <entry key="HEA" value-ref="headerRecordDescriptor" />
- <entry key="FOT" value-ref="footerRecordDescriptor" />
- <entry key="BCU" value-ref="businessCustomerLineDescriptor" />
- <entry key="NCU" value-ref="customerLineDescriptor" />
- <entry key="BAD" value-ref="billingAddressLineDescriptor" />
- <entry key="SAD" value-ref="shippingAddressLineDescriptor" />
- <entry key="BIN" value-ref="billingLineDescriptor" />
- <entry key="SIN" value-ref="shippingLineDescriptor" />
- <entry key="LIT" value-ref="itemLineDescriptor" />
- <entry key="" value-ref="defaultLineDescriptor" />
- </map>
- </property>
- </bean>
+
+
+
+
+ ]]>
This ensures that the line will be parsed correctly, which is
especially important for fixed length input. Any users of the
@@ -721,11 +721,11 @@
writing to a file. In Spring Batch this is the
LineAggregator:
- public interface LineAggregator {
+
+ }]]>
The LineAggregator is the opposite of a
LineTokenizer.
@@ -747,11 +747,11 @@
string, there needs to be an interface that describes how to convert
from an object into a FieldSet:
- public interface FieldSetCreator {
+
+ }]]>
As with LineTokenizer and
LineAggregator,
@@ -792,22 +792,22 @@
FlatFileItemWriter expresses this in
code:
- public void write(Object data) throws Exception {
+
+ }]]>
A simple configuration with the smallest ammount of setters
would look like the following:
- <bean id="itemWriter"
- class="org.springframework.batch.io.file.FlatFileItemWriter">
- <property name="resource"
- value="file:target/test-outputs/20070122.testStream.multilineStep.txt" />
- <property name="fieldSetCreator">
- <bean class="org.springframework.batch.io.file.mapping.PassThroughFieldSetMapper"/>
- </property>
- </bean>
+
+
+
+
+
+ ]]>
@@ -911,28 +911,28 @@
stream. First, lets examine a set of XML records that the
StaxEventItemReader can process.
-
-<?xml version="1.0" encoding="UTF-8"?>
-<records>
- <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
- <isin>XYZ0001</isin>
- <quantity>5</quantity>
- <price>11.39</price>
- <customer>Customer1</customer>
- </trade>
- <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
- <isin>XYZ0002</isin>
- <quantity>2</quantity>
- <price>72.99</price>
- <customer>Customer2c</customer>
- </trade>
- <trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
- <isin>XYZ0003</isin>
- <quantity>9</quantity>
- <price>99.99</price>
- <customer>Customer3</customer>
- </trade>
-</records>
+
+
+
+ XYZ0001
+ 5
+ 11.39
+ Customer1
+
+
+ XYZ0002
+ 2
+ 72.99
+ Customer2c
+
+
+ XYZ0003
+ 9
+ 99.99
+ Customer3
+
+]]>
To be able to process the XML records the following is needed:
@@ -954,22 +954,22 @@
- <property name="itemReader">
- <bean class="org.springframework.batch.io.xml.StaxEventItemReader">
- <property name="fragmentRootElementName" value="trade" />
- <property name="resource" value="data/staxJob/input/20070918.testStream.xmlFileStep.xml" />
- <property name="fragmentDeserializer">
- <bean class="org.springframework.batch.io.xml.oxm.UnmarshallingEventReaderDeserializer">
- <constructor-arg>
- <bean class="org.springframework.oxm.xstream.XStreamMarshaller">
- <property name="aliases" ref="aliases" />
- </bean>
- </constructor-arg>
- </bean>
- </property>
- </bean>
-</property>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
Notice that in this example we have chosen to use an
XStreamMarshaller that requires an alias passed
@@ -980,16 +980,16 @@
the map. In the configuration file we can use a spring configuration
utility to describe the required alias as follows:
-
- <util:map id="aliases">
- <entry key="trade"
- value="org.springframework.batch.sample.domain.Trade" />
- <entry key="isin" value="java.lang.String" />
- <entry key="quantity" value="long" />
- <entry key="price" value="java.math.BigDecimal" />
- <entry key="customer" value="java.lang.String" />
- </util:map>
-
+
+
+
+
+
+
+
+ ]]>
On input the reader reads the XML resource until it recognizes a
new fragment is about to start (by matching the tag name by default).
@@ -1003,7 +1003,7 @@
injection provided by the spring configuration would look something like
the following:
-
+
+]]>
@@ -1049,13 +1049,13 @@
MarshallingEventWriterSerializer. The Spring
configuration for this setup looks as follows:
- <bean class="org.springframework.batch.item.xml.StaxEventItemWriter" id="tradeStaxWriter">
- <property name="resource"value="file:target/test-outputs/20070918.testStream.xmlFileStep.output.xml" />
- <property name="serializer" ref="tradeMarshallingSerializer" />
- <property name="rootTagName" value="trades" />
- <property name="overwriteOutput" value="true" />
-</bean>
-
+
+
+
+
+
+
+]]>
The configuration sets up the three required properties and
optionally sets the overwriteOutput=true, mentioned earlier in the
@@ -1075,7 +1075,7 @@
all of the points discussed, demonstrating the programmatic setup of the
required properties.
- StaxEventItemWriter staxItemWriter = new StaxEventItemWriter()
+
+ staxItemWriter.flush()]]>
For a complete example configuration of XML input and output and a
corresponding Job see the sample xmlStaxJob.
@@ -1120,11 +1120,11 @@
java.io.File. Both XML and Flat File resources can
be configured using standard Spring constructs:
- <bean id="flatFileItemReader"
- class="org.springframework.batch.item.file.FlatFileItemReader">
- <property name="resource"
- value="file://outputs/20070122.testStream.CustomerReportStep.TEMP.txt" />
- </bean>
+
+
+ ]]>
The above Resource will load the file from
the file system, at the location specificied. Note that absolute locations
@@ -1134,10 +1134,10 @@
determined at runtime as a parameter to the job. This could be solved
using '-D' parameters, i.e. a system property:
- <bean id="flatFileItemReader"
- class="org.springframework.batch.item.file.FlatFileItemReader">
- <property name="resource" value="${input.file.name}" />
-</bean>
+
+
+]]>
All that would be required for this solution to work would be a
system argument (-Dinput.file.name="file://file.txt"). (Note that although
@@ -1154,10 +1154,10 @@
either job name, step name, or any values from the
JobParameters, by surrounding them with %:
- <bean id="inputFile"
- class="org.springframework.batch.core.resource.StepExecutionResourceProxy" />
- <property name="filePattern" value="//%JOB_NAME%/%STEP_NAME%/%file.name%" />
- </bean>
+
+
+ ]]>
Assuming a job name of 'fooJob', and a step name of 'fooStep', and
the key-value pair of 'file.name="fileName.txt"' is in the
@@ -1168,11 +1168,11 @@
StepExecution, it must be registered as a
StepListener:
- <bean id="fooStep" parent="abstractStep"
+
+ p:itemWriter-ref="itemWriter">
+
+ ]]>
The StepListener interface will be discussed
in more detail in Chapter 4. For now, it is sufficient to know that the
@@ -1188,20 +1188,20 @@
for both XML and FlatFile processing. Consider the following files in a
directory:
- file-1.txt file-2.txt ignored.txt
+
file-1.txt and file-2.txt are formatted the same and for business
reasons should be processed together. The
MuliResourceItemReader can be used to read in both
files by using wildcards:
-
- <bean id="multiResourceReader" class="org.springframework.batch.item.SortedMultiResourceItemReader">
- <property name="resources" value="classpath:data/multiResourceJob/input/file-*.txt" />
- <property name="delegate" ref="flatFileItemReader" />
- </bean>
+
+
+
+
-
+]]>
The referenced delegate is a simple
FlatFileItemReader. The above configuration will
@@ -1209,7 +1209,7 @@
should be noted that, as with any ItemReader, adding extra input (in this
case a file) could cause potential issues when restarting. It is
recommended that batch jobs work with their own individual directories
- until completed successfully.
+ until completed successfully.
@@ -1288,18 +1288,18 @@
DataSource. The following database schema will
be used as an example:
- CREATE TABLE CUSTOMER (
+
+);]]>
Many people prefer to use a domain object for each row, so we'll
use an implementation of the RowMapper
interface to map a CustomerCredit
object:
- public class CustomerCreditRowMapper implements RowMapper {
+
+}]]>
Because JdbcTemplate is so familiar to
users of Spring, and the JdbcCursorItemReader
@@ -1326,12 +1326,12 @@
CUSTOMER database. The first example will be using
JdbcTemplate:
-
+
+]]>
After running this code snippet the customerCredits list will
contain 1,000 CustomerCredit objects. In the
@@ -1342,7 +1342,7 @@
constrast this with the approach of the
JdbcCursorItemReader:
-
+
+]]>
After running this code snippet the counter will equal 1,000. If
the code above had put the returned customerCredit into a list, the
@@ -1473,7 +1473,7 @@
configuration using the same 'customer credit' example as the JDBC
reader:
-
+
+]]>
This configured ItemReader will return
CustomerCredit objects in the exact same manner
@@ -1502,6 +1502,55 @@
+
+ Paging ItemReaders
+
+ An alternative to using a database cursor is executing multiple
+ queries where each query is bringing back a portion of the results. We
+ refer to this portion as a page. Each query that is executed must
+ specify the starting row number and the number of rows that we want
+ returned for the page.
+
+
+ JpaPagingItemReader
+
+ Currently the only available implementation of a paging
+ ItemReader is the
+ JpaPagingItemReader. JPA doesn't have a concept
+ similar to the Hibernate StatelessSession so we
+ have to use other features provided by the JPA specification. SInce
+ JPA supports paging, this is a natural choice when it comes to using
+ JPA for batch processing. After each page is read the entities will
+ become detached and the persistence context will be cleared in order
+ to allow the entities to be garbage collected once the page is
+ processed.
+
+ The JpaPagingItemReader allows you to
+ declare a JPQL statement and pass in a
+ EntityManagerFactory. It will then pass back
+ one item per call to read in the same basic
+ fashion as any other ItemReader. The paging
+ happenes behind the scenes when additional entities are needed. Below
+ is an example configuration using the same 'customer credit' example
+ as the JDBC reader above:
+
+
+
+
+
+
+]]>
+
+ This configured ItemReader will return
+ CustomerCredit objects in the exact same manner
+ as described by the JdbcCursorItemReader above,
+ assuming the Customer object has the correct JPA annotations or ORM
+ mapping file. The 'pageSize' property determines the number of
+ entities read from the database for each query execution.
+
+
+
Driving Query Based ItemReaders
@@ -1563,12 +1612,12 @@
real complication is how those keys are obtained. The
KeyCollector interface abstracts this:
- public interface KeyCollector {
+
+ }]]>
The primary method in this interface is the
retrieveKeys method. It is expected that this
@@ -1583,12 +1632,12 @@
retrieveKeys method can then use this value
to retrieve a subset of the original keys:
- ExecutionContext executionContext = new ExecutionContext();
+
+ //keys should now contains 500 through 1,000]]>
This generalization illustrates the
KeyCollector contract. If we assume that
@@ -1651,36 +1700,36 @@
The following code helps illustrate how to setup and use a
SingleColumnJdbcKeyCollector:
- SingleColumnJdbcKeyCollector keyCollector = new SingleColumnJdbcKeyCollector(getJdbcTemplate(),
+ ? order by ID");
ExecutionContext executionContext = new ExecutionContext();
List keys = keyStrategy.retrieveKeys(new ExecutionContext());
- for (int i = 0; i < keys.size(); i++) {
+ for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i));
- }
+ }]]>
If this code were run in the proper environment with the correct
database tables setup, then it would output the following:
- 1
+
+5]]>
Now, let's modify the code slightly to show what would happen if
the code were started again after a restart, having failed after
processing key 3 successfully:
- SingleColumnJdbcKeyCollector keyCollector = new SingleColumnJdbcKeyCollector(getJdbcTemplate(),
+ ? order by ID");
ExecutionContext executionContext = new ExecutionContext();
@@ -1688,19 +1737,19 @@
List keys = keyStrategy.retrieveKeys(executionContext);
- for (int i = 0; i < keys.size(); i++) {
+ for (int i = 0; i < keys.size(); i++) {
System.out.println(keys.get(i));
- }
+ }]]>
Running this code snippet would result in the following:
- 4
-5
+
The key difference between the two examples is the following
line:
- keyStrategy.updateContext(new Long(3), executionContext);
+
This tells the key collector to update the provided
ExecutionContext with the key of three. This
@@ -1711,7 +1760,7 @@
ExecutionContext that was updated to contain 3,
the argument of 3 will be passed to the restartSql:
- keyCollector.setRestartSql("SELECT ID from T_FOOS where ID > ? order by ID");
+ ? order by ID");]]>
This will cause only keys 4 and 5 to be returned, since they are
the only ones with an ID greater than 3.
@@ -1738,13 +1787,13 @@
An ExecutionContextRowMapper provides
this:
- public interface ExecutionContextRowMapper extends RowMapper {
+
+]]>
The ExecutionContextRowMapper interface
extends the standard RowMapper interface to
@@ -1819,7 +1868,7 @@
won't be written out again. However, in this scenario, there's no way
for it to know which item caused the issue, the whole buffer was being
written out when the failure happened. The only way to solve this issue
- is to flush after each item:
+ is to flush after each item:
@@ -1842,18 +1891,36 @@
items to be skipped reliably. The following example illustrates how to
configure the HibernateAwareItemWriter:
- <bean id="hibernateItemWriter"
- class="org.springframework.batch.item.database.HibernateAwareItemWriter">
- <property name="sessionFactory" ref="sessionFactory" />
- <property name="delegate" ref="customerCreditWriter" />
- </bean>
+
+
+
+
- <bean id="customerCreditWriter"
- class="org.springframework.batch.sample.dao.HibernateCreditDao">
- <property name="sessionFactory" ref="sessionFactory" />
- </bean>
+
+
+
-
+]]>
+
+ If you are using JPA then the
+ JpaAwareItemWriter provides comparable
+ functionality. The following example illustrates how to configure the
+ JpaAwareItemWriter:
+
+
+
+
+
+
+
+
+
+
+]]>
@@ -1878,12 +1945,12 @@
standard Spring method invoking delegator pattern and are fairly simple to
set up. Below is an example of the reader:
- <bean id="itemReader" class="org.springframework.batch.item.adapter.ItemReaderAdapter">
- <property name="targetObject" ref="fooService" />
- <property name="targetMethod" value="generateFoo" />
- </bean>
+
+
+
+
- <bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
+ ]]>
One important point to note is that the contract of the targetMethod
must be the same as the contract for read: when
@@ -1894,13 +1961,13 @@
ItemWriter. The ItemWriter
implementation is equally as simple:
- <bean id="itemWriter" class="org.springframework.batch.item.adapter.ItemWriterAdapter">
- <property name="targetObject" ref="fooService" />
- <property name="targetMethod" value="processFoo" />
- </bean>
+
+
+
+
- <bean id="fooService" class="org.springframework.batch.item.sample.FooService" />
-
+
+]]>
@@ -1915,7 +1982,7 @@
that contains another ItemReader. For
example:
- public class CompositeItemWriter implements ItemWriter {
+
+}]]>
The class above contains another ItemWriter
that it delgates to after having provided some business logic. It should
@@ -1954,10 +2021,10 @@
just want to modify the item. For this scenario, Spring Batch provides the
ItemTransformer interface:
- public interface ItemTransformer {
+
+ }]]>
An ItemTransformer is very simple, given one
object, transorm it and return another. The object provided may or may not
@@ -1973,7 +2040,7 @@
ItemTransformer can be written that performs the
conversion:
- public class Foo {}
+
+ }]]>
In the very simple example above, there is a class
Foo, a class Bar, and a
@@ -2013,31 +2080,29 @@
Bar returned. The resulting
Bar will then be written:
- ItemTransformerItemWriter itemTransformerItemWriter = new ItemTransformerItemWriter();
+
+ itemTransformerItemWriter.write(new Foo());]]>
The Delegate Pattern and Registering with the Step
-
- Note that the ItemTransformerItemWriter
- and the CompositeItemWriter are examples of a
- delegation pattern, which is common in Spring Batch. The delegates
- themselves might implement callback interfaces like
- ItemStream or
- StepListener. If they do, and they are being
- used in conjunction with Spring Batch Core as part of a
- Step in a Job, then they
- almost certainly need to be registered manually with the
- Step. Registration is automatic when using the
- factory beans (*StepFactoryBean) , but only for
- the ItemReader and
- ItemWriter injected directly. The delegates are
- not known to the Step, so they need to be
- injected as listeners or streams (or both if appropriate).
-
+ Note that the ItemTransformerItemWriter and
+ the CompositeItemWriter are examples of a
+ delegation pattern, which is common in Spring Batch. The delegates
+ themselves might implement callback interfaces like
+ ItemStream or
+ StepListener. If they do, and they are being used
+ in conjunction with Spring Batch Core as part of a
+ Step in a Job, then they
+ almost certainly need to be registered manually with the
+ Step. Registration is automatic when using the
+ factory beans (*StepFactoryBean) , but only for
+ the ItemReader and
+ ItemWriter injected directly. The delegates are
+ not known to the Step, so they need to be
+ injected as listeners or streams (or both if appropriate).
@@ -2051,7 +2116,7 @@
Transformed to Bar, which will be transformed to
Foobar and written out:
- public class Foo {}
+
+ }]]>
A FooTransformer and
BarTransformer can be 'chained' together to give
the resultant Foobar:
- CompositeItemTransformer compositeTransformer = new CompositeItemTransformer();
+
+ compositeTransformer.setItemTransformers(itemTransformers);]]>
The compositeTransformer could be said to accept a
Foo and return a Foobar.
@@ -2132,11 +2197,11 @@
rather provides a very simple interface that can be implemented by any
number of frameworks:
- public interface Validator {
+
+ }]]>
The contract is that the validate method
will throw an exception if the object is invalid, and return normally if
@@ -2179,21 +2244,19 @@
The Delegate Pattern and Registering with the Step
-
- Note that the ValidatingItemReader is
- another example of a delegation pattern, and the delegates themselves
- might implement callback interfaces like
- ItemStream or
- StepListener. If they do, and they are being
- used in conjunction with Spring Batch Core as part of a step in a job,
- then they almost certainly need to be registered manually with the
- Step. Registration is automatic when using the
- factory beans (*StepFactoryBean) , but only for
- the ItemReader and
- ItemWriter injected directly - the delegates
- are not known to the step, so they need to be injected as listeners or
- streams (or both if appropriate).
-
+ Note that the ValidatingItemReader is
+ another example of a delegation pattern, and the delegates themselves
+ might implement callback interfaces like
+ ItemStream or
+ StepListener. If they do, and they are being used
+ in conjunction with Spring Batch Core as part of a step in a job, then
+ they almost certainly need to be registered manually with the
+ Step. Registration is automatic when using the
+ factory beans (*StepFactoryBean) , but only for
+ the ItemReader and
+ ItemWriter injected directly - the delegates are
+ not known to the step, so they need to be injected as listeners or
+ streams (or both if appropriate).
@@ -2267,7 +2330,7 @@
basic contract of ItemReader,
read:
- public class CustomItemReader implements ItemReader{
+
+ }]]>
This very simple class takes a list of items, and returns one at a
time, removing it from the list. When the list empty, it returns null,
thus satisfying the most basic requirements of an
ItemReader, as illustrated below:
- List items = new ArrayList();
+
+ assertNull(itemReader.read());]]>
Making the ItemReader
@@ -2319,7 +2382,7 @@
empty, but we'll need to add code to them in order to support the
rollback scenario:
- public class CustomItemReader implements ItemReader{
+
+ }]]>
The CustomItemReader has now been
modified to keep track of where it is currently, and where it was when
@@ -2355,12 +2418,12 @@
ItemReader to the state it was in when
mark was last called:
- //Assume same setup as last example, a list with "1", "2", and "3"
+
+ assertEquals("1", itemReader.read());]]>
In most real world scenarios, there will likely be some kind of
underlying resource that will require tracking. In the case of a file,
@@ -2402,7 +2465,7 @@
implemented with the ItemStream
interface:
- public class CustomItemReader implements ItemReader, ItemStream{
+
+ }]]>
On each call to ItemStream
update method, the current index of the
@@ -2457,7 +2520,7 @@
current index is moved to that location. This is a fairly trivial
example, but it still meets the general contract:
- ExecutionContext executionContext = new ExecutionContext();
+
+ assertEquals("2", itemReader.read());]]>
Most ItemReaders have much more sophisticated restart logic. The
DrivingQueryItemReader, for example, only loads
@@ -2500,7 +2563,7 @@
example. As with the ItemReader example, a List
will be used in order to keep the example as simple as possible:
- public class CustomItemWriter implements ItemWriter{
+
+ }]]>
Making the ItemReader
@@ -2531,7 +2594,7 @@
flush and clear
should be implemented to allow for a buffering solution:
- public class CustomItemWriter implements ItemWriter{
+
+ }]]>
The ItemWriter buffers all output, only
writing to the actual output (in this case by added to a list) when