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

Douglas Surber douglas.surber at
Thu Oct 26 16:56:47 UTC 2017

ArrayCountOperation and OperationGroup already done. (BatchCountOperation has been replaced by ArrayCountOperation.)

Example of dependency on T:

    String sql = "call generate_paycheck(?, ?)";
            .set("1", emp, JdbcType.STRUCT)
            .outParameter("2", JdbcType.STRUCT)
            .resultProcessor(m -> m.get("2", CheckDetail.class))
            .thenAccept( (d) -> { printPaycheck(d); });

Again, I don’t like the necessity of the type witness, but I have no problem with the notion of saying that idiomatic ADBA places a type witness before every call to an Operation factory method. I have had no problem adopting that idiomatic usage in writing example code. I just type “conn.<TypeWitness>fooOperation(…” almost automatically every time. No, it is not ideal but I do not consider it a huge problem.


> On Oct 26, 2017, at 9:23 AM, Lukas Eder <lukas.eder at> wrote:
> That's really cool, thanks for adopting this!
> I'm thinking maybe this should then be done in BatchCountOperation as well (replacing initialValue() and countAggregator()) and perhaps in OperationGroup (replacing initialValue() and memberAggregator())?
> 2017-10-25 23:16 GMT+03:00 Douglas Surber <douglas.surber at <mailto:douglas.surber at>>:
> 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);
> May I try my luck again regarding the generics involved here. I'm still having a bit of a hard time understanding why the <T> type has to be bound (and fixed!) at the moment when an Operation<T> subtype is created from the OperationGroup.
> From how I understand the API, each Operation<T> has a type <T> which may or may not be well defined at some point. Initially, it is not defined because we may submit operations that do not wish to retrieve any results. This might be the case when you return "null" in some of your examples. A good type bound for <T> would then be Void or just "?".
> Then, some operations have type-specifying methods, like the above collect() method. From the moment when we call collect(), the type <T> could be inferred from the Collector. So, after calling collect() on any RowOperation<X>, we "change" or "specify" the returning type to RowOperation<T>.
> Something similar could be said of CountOperation.resultProcessor(), which could infer its new <T> type from the argument function, rather than requiring the function to adhere to some previously defined type.
> I understand that this all originates from the OperationGroup<S, T> type, which imposes the <T> type on its children, but perhaps I simply fail to understand the intent of the OperationGroup.memberAggregator() method, which is probably the target of these <S, T> types. Do you have an example where that is put to action?
> 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.
> Yes, I've thought about this as well, afterwards. I could imagine databases to open up several sockets to fetch data from a parallel query. That could be super cool. I've just noticed CollectionGroup.parallel(). I guess that's related, then?

More information about the jdbc-spec-discuss mailing list