I forgot to post what I ended up with, which is a custom ItemReader.
I created a class implementing ResourceAwareItemReaderItemStream. The class also has a delegate of type PeekableItemReader.
I called my class FieldBasedAggregatingReader. You configure it with one or more fields that are the keys to indicate when to stop reading. It calls peek() on the delegate, checks whether the key fields have changed from the first record read in the current call to read(). In pseudo-code ...
Code:
List<?> read() {
currentRecord = delegate.peek()
initialValues = extractKeyFields(currentRecord)
while (!done) {
done = compareCurrentAndInitial(currentRecord, initialValues)
if (!done) {
add current record to list of results
delegate.read() to advance delegate to next record
currentRecord = delegate.peek()
if (currentRecord == null) {
done = true
delegate.read() to advance delegate to next record
}
}
return list of results
}
So, each call to read() on my class returns 1 record, which is a list of 0..n records from the underlying reader, where all the records have the same key values.