Replace RowOperation.initialValue() and rowAggregator() by collect(Collector)

Douglas Surber douglas.surber at
Wed Oct 25 20:16:39 UTC 2017

I’ve thought a bit more about your suggestion. It would add the following to RowOperation.

    public <A> RowOperation<T> collect(Collector<? super Result.Row, A, ? extends T> c);

I think it is better than initialValue, rowAggregator. You questioned the value of the combiner method. That is one of the things that makes this superior. While the implementation is, in general, constrained to produce the rows in the same order as the database, a Collector can be UNORDERED and/or CONCURRENT. The spec could allow an implementation to use that information to process the rows out of order and/or in multiple threads. This is a big win.

Consider your suggestion adopted. Many thanks.


> On Oct 25, 2017, at 7:17 AM, Lukas Eder <lukas.eder at> wrote:
> In this email, I will make two suggestions:
> - Replacing initialValue() and rowAggregator() by collect(Collector)
> - Improving the API to avoid the need for unidiomatic type witnesses
> While playing around with the ADBA API, it struck me that the
> RowOperation.initialValue() and rowAggregator() methods are not as
> versatile as they could be. In particular, the mechanism reminded me of the
> API where:
> - RowOperation.initialValue() corresponds to Collector.supplier()
> - RowOperation.rowAggregator() corresponds to Collector.accumulator()
> And these two Collector methods don't have any correspondence in ADBA:
> - Collector.combiner()
> - Collector.finisher()
> If we think of this "row aggregation" as an act of collecting all rows into
> a target type, we might be able to find some more interesting features
> where the Stream API and the new ADBA API can work together, even if ADBA
> is asynchronous, push-oriented and Stream is synchronous, pull-oriented.
> So, we could have: <A, R> RowOperation<R> collect(Collector<? super Row, A,
> R>)
> Note that I made this method suggestion generic as one thing that has
> always bothered me in the examples were the not-so-idiomatic type witnesses
> in examples like this one:
> conn
>    .<List<Integer>>rowOperation(sql)
>    .set("target", 42, JdbcType.NUMERIC)
>    .initialValue( () -> new ArrayList<>() )
>    .rowAggregator( (list, row) -> {
>        list.add(row.get("id", Integer.class));
>        return list;
>    } )
>    .submit();
> We could have, instead:
> conn
>    .rowOperation(sql)
>    .set("target", 42, JdbcType.NUMERIC)
>    .collect(Collector.of(
>        () -> new ArrayList<Integer>(),
>        (list, row) -> { ... }, // unchanged
>        (list1, list2) -> { ... }
>    ))
>    .submit();
> I understand that there isn't a complete match between the two paradigms.
> In particular, the Collector.combiner() doesn't make much sense in the
> absence of parallelism, but the Collector.finisher() could be quite useful
> in some situations where the intermediate, mutable accumulator is something
> like int[], i.e. not a type that we want to return to the calling operation.
> Thoughts?

More information about the jdbc-spec-discuss mailing list