OPEN - issue BATCH-767: Update documentation for M2
http://jira.springframework.org/browse/BATCH-767 First pass through chapters 2, 3 and Appendix A.
This commit is contained in:
Binary file not shown.
@@ -25,12 +25,14 @@
|
||||
|
||||
<tbody>
|
||||
<row>
|
||||
<entry align="left">AbstractBufferedItemReaderItemStream</entry>
|
||||
<entry
|
||||
align="left">AbstractItemCountingItemStreamItemReader</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">Abstract base class that provides bufferring
|
||||
functionality for any subclass.</entry>
|
||||
<entry align="left">Abstract base class that provides basic
|
||||
restart capabilities by counting the number of items returned from
|
||||
an <classname>ItemReader</classname>.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -42,15 +44,6 @@
|
||||
time</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">ValidatingItemReader</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">A simple extension of DelegatingItemReader
|
||||
that provides for validation before returning input.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">ItemReaderAdapter</entry>
|
||||
|
||||
@@ -75,18 +68,6 @@
|
||||
role="bold">END_RECORD</emphasis></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">DelegatingItemReader</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">Extends AbstractMethodInvokingDelegator, which
|
||||
enables dynamically calling of a custom method of the injected
|
||||
object. Provides a convenient API for dynamic method invocation
|
||||
shielding subclasses from the low-level details and exception
|
||||
handling.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">FlatFileItemReader</entry>
|
||||
|
||||
@@ -153,6 +134,26 @@
|
||||
received through the injected JmsOperations receive()
|
||||
method</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">JpaPagingItemReader</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">Given a JPQL statement, pages through the
|
||||
rows, such that large datasets can be read without running out of
|
||||
memory</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">JdbcPagingItemReader</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">Given a SQL statement, pages through the rows,
|
||||
such that large datasets can be read without running out of
|
||||
memory</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
@@ -220,16 +221,6 @@
|
||||
role="bold">ItemWriter</emphasis> objects</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">DelegatingItemWriter</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">Wraps ItemWriter and is BeanAware allowing it
|
||||
to respond to Spring Bean events like
|
||||
afterPropertiesSet().</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">ItemWriterAdapter</entry>
|
||||
|
||||
@@ -251,17 +242,6 @@
|
||||
name</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">ItemTransformerItemWriter</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">Extends <emphasis
|
||||
role="bold">DelegatingItemWriter</emphasis> by defining its
|
||||
<emphasis role="bold">doProcess</emphasis> method to call an
|
||||
injected <emphasis role="bold">ItemTransformer</emphasis></entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">FlatFileItemWriter</entry>
|
||||
|
||||
@@ -270,8 +250,8 @@
|
||||
<entry align="left">Attempts to convert the item to a <emphasis
|
||||
role="bold">String</emphasis>, <emphasis
|
||||
role="bold">Collection</emphasis> or array using an injected
|
||||
<emphasis role="bold">Coverter</emphasis> and then recurses. See
|
||||
[HOWTO - Write to a File]</entry>
|
||||
<emphasis role="bold">Coverter</emphasis> and then
|
||||
recurses.</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -282,8 +262,19 @@
|
||||
<entry align="left">This item writer is hibernate session aware
|
||||
and handles some transaction-related work that a non-"hibernate
|
||||
aware" item writer would not need to know about and then delegates
|
||||
to another item writer to do the actual writing. See [HOWTO -
|
||||
Write to a Database]</entry>
|
||||
to another item writer to do the actual writing. </entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<entry align="left">JpaAwareItemWriter</entry>
|
||||
|
||||
<entry align="left">java.lang.Object</entry>
|
||||
|
||||
<entry align="left">This item writer is JPA EntityManager aware
|
||||
and handles some transaction-related work that a non-"jpa aware"
|
||||
<classname>ItemWriter</classname> would not need to know about and
|
||||
then delegates to another writer to do the actual writing.
|
||||
</entry>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
@@ -294,7 +285,7 @@
|
||||
<entry align="left">Uses an <emphasis
|
||||
role="bold">ObjectToXmlSerializer</emphasis> implementation to
|
||||
convert each item to XML and then writes it to an XML file using
|
||||
StAX. See [HOWTO - Write to a File]</entry>
|
||||
StAX.</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
|
||||
@@ -605,9 +605,9 @@
|
||||
<classname>Step</classname> interface. Two step implementation classes
|
||||
are available in the Spring Batch framework, and they are each discussed
|
||||
in detail in Chatper 4 of this guide. For most situations, the
|
||||
<classname>StepHandlerStep</classname> implementation is sufficient,
|
||||
but for situations where only one call is needed, such as a stored
|
||||
procedure call or a wrapper around existing script, a
|
||||
<classname>StepHandlerStep</classname> implementation is sufficient, but
|
||||
for situations where only one call is needed, such as a stored procedure
|
||||
call or a wrapper around existing script, a
|
||||
<classname>TaskletStep</classname> may be a better option.</para>
|
||||
</section>
|
||||
|
||||
@@ -1040,6 +1040,19 @@
|
||||
various implementations can be found in Chapter 3.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Item Processor</title>
|
||||
|
||||
<para><classname>ItemProcessor</classname> is an abstraction that
|
||||
represents the business processing of an item. While the
|
||||
<classname>ItemReader</classname> reads one item, and the
|
||||
<classname>ItemWriter</classname> writes them, the
|
||||
<classname>ItemProcessor</classname> provides access to transform or apply
|
||||
other business processing. If while processing the item it's determined
|
||||
that it's not valid, returning null indicates that it should not be
|
||||
written out.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title id="s.2.1.6">Tasklet</title>
|
||||
|
||||
@@ -1050,4 +1063,4 @@
|
||||
logic that is not natural to split into read-(transform)-write phases,
|
||||
such as invoking a system command or a stored procedure.</para>
|
||||
</section>
|
||||
</chapter>
|
||||
</chapter>
|
||||
@@ -52,11 +52,8 @@
|
||||
|
||||
<programlisting>public interface ItemReader<T> {
|
||||
|
||||
T read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException;
|
||||
T read() throws Exception, UnexpectedInputException, ParseException;
|
||||
|
||||
void mark() throws MarkFailedException;
|
||||
|
||||
void reset() throws ResetFailedException;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
@@ -68,13 +65,11 @@
|
||||
(i.e. Trade, Foo, etc) but there is no requirement in the contract to do
|
||||
so.</para>
|
||||
|
||||
<para>The <methodname>mark</methodname> and <methodname>reset</methodname>
|
||||
methods are important due to the transactional nature of batch processing.
|
||||
Mark() will be called before reading begins. Calling
|
||||
<methodname>reset</methodname> at anytime will position the
|
||||
<classname>ItemReader</classname> to its position when
|
||||
<methodname>mark</methodname> was last called. The semantics are very
|
||||
similar to <classname>java.io.Reader</classname>.</para>
|
||||
<para>It is expected that implementations of the
|
||||
<classname>ItemReader</classname> interface will be forward only. However,
|
||||
if the underlying resource is transactional (such as a JMS queue) thehn
|
||||
calling read may return the same logical item on subsequent calls in a
|
||||
rollback scenario.</para>
|
||||
|
||||
<para>It is also worth noting that a lack of items to process by an
|
||||
<classname>ItemReader</classname> will not cause an exception to be
|
||||
@@ -99,31 +94,22 @@
|
||||
|
||||
<programlisting>public interface ItemWriter<T> {
|
||||
|
||||
void write(T item) throws Exception;
|
||||
void write(List<? extends T> items) throws Exception;
|
||||
|
||||
void flush() throws FlushFailedException;
|
||||
|
||||
void clear() throws ClearFailedException;
|
||||
}
|
||||
</programlisting>
|
||||
|
||||
<para>As with <methodname>read</methodname> on
|
||||
<classname>ItemReader</classname>, <methodname>write</methodname> provides
|
||||
the basic contract of <classname>ItemWriter</classname>, it will attempt
|
||||
to write out the item passed in as long as it is open. As with
|
||||
<methodname>mark</methodname> and <methodname>reset</methodname>,
|
||||
<methodname>flush</methodname> and <methodname>clear</methodname> are
|
||||
necessary due to the transactional nature of batch processing. Because it
|
||||
is generally expected that items will be 'batched' together into a chunk,
|
||||
and then output, it is expected that an <classname>ItemWriter</classname>
|
||||
will perform some type of buffering. <methodname>flush</methodname> will
|
||||
empty the buffer by writing the items out, whereas
|
||||
<methodname>clear</methodname> will simply throw the contents of the
|
||||
buffer away. In most cases, a <classname>Step</classname> implementation
|
||||
will call <methodname>flush</methodname> before a commit and
|
||||
<methodname>clear</methodname> in case of rollback. It is expected that
|
||||
implementations of the <classname>Step</classname> interface will call
|
||||
these methods.</para>
|
||||
to write out the list of items passed in as long as it is open. Because it
|
||||
is generally expected that items will be 'batched' together into a chunk
|
||||
and then output, the interface accepts a list, rather than an item by
|
||||
itself. After writing out the list, any flushing that may be necessary can
|
||||
be performed before returning from the write method. For example, if
|
||||
writing to a Hibernate DAO, multiple calls to write can be made, one for
|
||||
each item. The writer can then call close on the hibernate Session before
|
||||
returning.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
@@ -324,6 +310,18 @@
|
||||
</tgroup>
|
||||
</table></para>
|
||||
|
||||
<section>
|
||||
<title>LineMapper</title>
|
||||
|
||||
<para>As with <classname>RowMapper</classname>, which takes a low
|
||||
level construct such as <classname>ResultSet and returns an Object,
|
||||
flat file procesing requires the same construct to convert a String
|
||||
line into an Object:</classname><programlisting>public interface LineMapper<T> {
|
||||
|
||||
T mapLine(String line, int lineNumber) throws Exception;
|
||||
}</programlisting></para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>FieldSetMapper</title>
|
||||
|
||||
@@ -2315,10 +2313,6 @@
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void mark() throws MarkFailedException { };
|
||||
|
||||
public void reset() throws ResetFailedException { };
|
||||
}</programlisting>
|
||||
|
||||
<para>This very simple class takes a list of items, and returns one at a
|
||||
@@ -2337,83 +2331,6 @@
|
||||
assertEquals("3", itemReader.read());
|
||||
assertNull(itemReader.read());</programlisting>
|
||||
|
||||
<section>
|
||||
<title>Making the <classname>ItemReader</classname>
|
||||
transactional</title>
|
||||
|
||||
<para>This most basic <classname>ItemReader</classname> will work, but
|
||||
what happens if the transaction needs to be rolled back? This will
|
||||
usually caused by an error in the ItemWriter, since the ItmReader
|
||||
generally won't do anything that invalidates the transaction, but
|
||||
without supporting it, there would be erroneous results. ItemReaders
|
||||
are notified about rollbacks via the <methodname>mark</methodname> and
|
||||
<methodname>reset</methodname> methods. In the example above they're
|
||||
empty, but we'll need to add code to them in order to support the
|
||||
rollback scenario:</para>
|
||||
|
||||
<programlisting> public class CustomItemReader<T> implements ItemReader<T>{
|
||||
|
||||
List<T> items;
|
||||
int currentIndex = 0;
|
||||
int lastMarkedIndex = 0;
|
||||
|
||||
public CustomItemReader(List<T> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public T read() throws Exception, UnexpectedInputException,
|
||||
NoWorkFoundException, ParseException {
|
||||
|
||||
if (currentIndex < items.size()) {
|
||||
return items.get(currentIndex++);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void mark() throws MarkFailedException {
|
||||
lastMarkedIndex = currentIndex;
|
||||
};
|
||||
|
||||
public void reset() throws ResetFailedException {
|
||||
currentIndex = lastMarkedIndex;
|
||||
};
|
||||
}</programlisting>
|
||||
|
||||
<para>The <classname>CustomItemReader</classname> has now been
|
||||
modified to keep track of where it is currently, and where it was when
|
||||
mark() was last called. This allows the new
|
||||
<classname>ItemReader</classname> to fulfill the basic contract that
|
||||
calling <methodname>reset</methodname> returns the
|
||||
<classname>ItemReader</classname> to the state it was in when
|
||||
<methodname>mark</methodname> was last called:</para>
|
||||
|
||||
<programlisting> //Assume same setup as last example, a list with "1", "2", and "3"
|
||||
itemReader.mark();
|
||||
assertEquals("1", itemReader.read());
|
||||
assertEquals("2", itemReader.read());
|
||||
itemReader.reset();
|
||||
assertEquals("1", itemReader.read());</programlisting>
|
||||
|
||||
<para>In most real world scenarios, there will likely be some kind of
|
||||
underlying resource that will require tracking. In the case of a file,
|
||||
<methodname>mark</methodname> will hold the current location within
|
||||
the file, and <methodname>reset</methodname> will move it back. The
|
||||
<classname>JdbcCursorItemReader</classname>, for example, holds on to
|
||||
the current row number, and on reset moves the cursor back by calling
|
||||
the <classname> ResultSet</classname>
|
||||
<methodname>absolute</methodname> method, which moves the current
|
||||
cursor to the row number supplied. The
|
||||
<classname>CustomItemReader</classname> now completely adheres to the
|
||||
entire <classname>ItemReader</classname> contract.
|
||||
<methodname>read</methodname> will return the appropriates items,
|
||||
returning null when empty, and <methodname>reset</methodname> returns
|
||||
the <classname>ItemReader</classname> back to it's state as of the
|
||||
last call to <methodname>mark</methodname>, allowing for correct
|
||||
support of a rollback. (It's assumed a <classname>Step</classname>
|
||||
implementation will call <methodname>mark</methodname> and
|
||||
<methodname>reset</methodname>).</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Making the <classname>ItemReader</classname>
|
||||
restartable</title>
|
||||
@@ -2434,11 +2351,10 @@
|
||||
implemented with the <classname>ItemStream</classname>
|
||||
interface:</para>
|
||||
|
||||
<programlisting> public class CustomItemReader<T> implements ItemReader<T>, ItemStream{
|
||||
<programlisting> public class CustomItemReader<T> implements ItemReader<T>, ItemStream {
|
||||
|
||||
List<T> items;
|
||||
int currentIndex = 0;
|
||||
int lastMarkedIndex = 0;
|
||||
private static final String CURRENT_INDEX = "current.index";
|
||||
|
||||
public CustomItemReader(List<T> items) {
|
||||
@@ -2446,32 +2362,22 @@
|
||||
}
|
||||
|
||||
public T read() throws Exception, UnexpectedInputException,
|
||||
NoWorkFoundException, ParseException {
|
||||
ParseException {
|
||||
|
||||
if (currentIndex < items.size()) {
|
||||
return items.get(currentIndex++);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void mark() throws MarkFailedException {
|
||||
lastMarkedIndex = currentIndex;
|
||||
}
|
||||
|
||||
public void reset() throws ResetFailedException {
|
||||
currentIndex = lastMarkedIndex;
|
||||
}
|
||||
|
||||
public void open(ExecutionContext executionContext) throws ItemStreamException {
|
||||
|
||||
if(executionContext.containsKey(CURRENT_INDEX)){
|
||||
currentIndex = new Long(executionContext.getLong(CURRENT_INDEX)).intValue();
|
||||
}
|
||||
else{
|
||||
currentIndex = 0;
|
||||
lastMarkedIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close(ExecutionContext executionContext) throws ItemStreamException {}
|
||||
@@ -2534,65 +2440,19 @@
|
||||
example. As with the <classname>ItemReader</classname> example, a List
|
||||
will be used in order to keep the example as simple as possible:</para>
|
||||
|
||||
<programlisting> public class CustomItemWriter<T> implements ItemWriter<T>{
|
||||
<programlisting> public class CustomItemWriter<T> implements ItemWriter<T> {
|
||||
|
||||
List<T> output = new ArrayList<T>();
|
||||
List<T> output = TransactionAwareProxyFactory.createTransactionalList();
|
||||
|
||||
public void write(T item) throws Exception {
|
||||
output.add(item);
|
||||
public void write(List<? extends T> items) throws Exception {
|
||||
output.addAll(items);
|
||||
}
|
||||
|
||||
public void clear() throws ClearFailedException { }
|
||||
|
||||
public void flush() throws FlushFailedException { }
|
||||
}</programlisting>
|
||||
|
||||
<section>
|
||||
<title>Making the <classname>ItemReader</classname>
|
||||
transactional</title>
|
||||
|
||||
<para>The example is extremely simple, but it's worth showing to
|
||||
illustrate an <classname>ItemWriter</classname> that doesn't respond
|
||||
to rollbacks and commits (i.e. <methodname>clear</methodname> and
|
||||
<methodname>flush</methodname>). If your potential writer is such that
|
||||
it doesn't need to care about rollback or commit, likely because it's
|
||||
writing to a database, then there is little value to the
|
||||
<classname>ItemWriter</classname> interface in that scenario other
|
||||
than using it to meet another class's requirement for an
|
||||
implementation of the <classname>ItemWriter</classname> interface. In
|
||||
that case, the <classname>ItemWriterAdapter</classname> would be a
|
||||
better solution. However, if it does need to be transactional, then
|
||||
<methodname>flush</methodname> and <methodname>clear</methodname>
|
||||
should be implemented to allow for a buffering solution:</para>
|
||||
|
||||
<programlisting> public class CustomItemWriter<T> implements ItemWriter<T>{
|
||||
|
||||
List<T> output = new ArrayList<T>();
|
||||
List<T> buffer = new ArrayList<T>();
|
||||
|
||||
public void write(T item) throws Exception {
|
||||
buffer.add(item);
|
||||
}
|
||||
|
||||
public void clear() throws ClearFailedException {
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
public void flush() throws FlushFailedException {
|
||||
for(T t:buffer){
|
||||
output.add(t);
|
||||
}
|
||||
public List<T> getOutput() {
|
||||
return output;
|
||||
}
|
||||
}</programlisting>
|
||||
|
||||
<para>The <classname>ItemWriter</classname> buffers all output, only
|
||||
writing to the actual output (in this case by added to a list) when
|
||||
the <classname>ItemWriter</classname> <methodname>flush()</methodname>
|
||||
method is called. The contents of the buffer are thrown away when
|
||||
<classname>ItemWriter</classname> <methodname>clear</methodname>() is
|
||||
called.</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Making the <classname>ItemWriter</classname>
|
||||
restartable</title>
|
||||
|
||||
Reference in New Issue
Block a user