convenience methods
Douglas Surber
douglas.surber at oracle.com
Wed Nov 22 17:24:02 UTC 2017
Lukas,
Thanks for your reply and encouragement. I’m not going to undertake changes to JDBC. My plate is full with ADBA. If you contribute specific JDBC convenience methods to this list the EG may consider them for a future release. The less work the EG and the Spec Lead have to do the more likely your contributions are to be adopted. I would recommend providing the complete method source including the JavaDoc so that all the Spec Lead has to do is paste it into the appropriate source file. That’s not to say such contributions would necessarily be adopted verbatim or even at all, but it does increase the chances of getting something.
The same recommendation applies to ADBA convenience methods. The more complete your contribution the easier it is for us to adopt them. Even just a list of desired convenience method signatures would be useful for ADBA though. We can’t think of everything.
Douglas
> On Nov 22, 2017, at 3:28 AM, Lukas Eder <lukas.eder at gmail.com> wrote:
>
> Hi Douglas,
>
> I think that convenience methods are really very very helpful. jOOQ has tons of them, for instance, because that really greatly reduces boiler plate code on the call site and makes using the API much more effective and fun. This also helps increase adoption.
>
> One of the main reasons why Spring's JdbcTemplate, or Apache DbUtils, or the simpler parts of the jOOQ API exists is the fact that the (synchronous) JDBC API doesn't have any such methods. For instance, it would make total sense to have
>
> public interface Connection {
> default PreparedStatement prepareStatement(String sql, Object... bindings) {
> PreparedStatement s = prepareStatement(sql);
> for (int i = 0; i < bindings.length; i++)
> s.setObject(i + 1, bindings[i]);
> }
>
> default int executeUpdate(String sql, Object... bindings) {
> try (PreparedStatement s = prepareStatement(sql, bindings)) {
> return s.executeUpdate();
> }
> }
> }
>
> I mean, who really enjoys counting bind indexes manually and sending a very simple static INSERT statement with 10 values to the server by setting each bind variable individually?
>
> This convenience stuff has been dearly missing from the very first version of JDBC :) (granted, it would have been difficult to add, without default methods). There are many other possible convenience methods, including:
>
> public interface ResultSet {
> default Object[] get() {
> Object[] result = new Object[getMetaData().getColumnCount()];
>
> for (int i = 0; i < result.length; i++)
> result[i] = getObject(i);
>
> return result;
> }
>
> default void forEach(Consumer<Object[]> consumer) {
> while (next())
> consumer.accept(get());
> }
> }
>
> I could go on and on, if you're interested. In fact, consider this a feature request! :)
>
> Further comments inline:
>
> 2017-11-16 17:25 GMT+01:00 Douglas Surber <douglas.surber at oracle.com <mailto:douglas.surber at oracle.com>>:
> As default methods these are all implemented by calling other interface methods. Clearly they aren’t necessary as the app can make the same sequence of calls. On the other hand their use will result in more compact and more readable code. Also, some of them handle some not so obvious behavior so that the programmer doesn’t have to think about it in the common use case. Again DataSource.getConnection is a good example;
>
> Yes, a very good example. No one wants to type that builder() stuff all the time even if it is useful for the special case. The convenience API should be there for the more general case.
>
> look at Connection.connect. A third benefit and honestly the original motivation for many of them is as documentation, how to use the API.
>
> That's an interesting way to look at it - it never occurred to me but you're absolutely right. It's a nice way to document lower level API parts.
>
> As a general notion, do these methods carry their weight? Are the benefits they provide worth whatever additional complexity they add?
>
> Absolutely! Again, one of the biggest flaws of the classic JDBC API is the verbosity it generates in client code. Now many people argue that verbosity is Java's strength, but that's nonsense when it comes to infrastructure logic that never changes. E.g. try-with-resources is "convenience API" that really really helped JDBC, because the proper approach to closing all of Connection, Statement, ResultSet in the right order and with the right exception handling really doesn't add any value to anyone's business - au contraire, it's too easy to get them wrong.
>
> Languages like Kotlin don't have the try-with-resources statement, but extension methods that can be attached to any (Auto)Closeable, which is the same thing as *external* convenience API. Without extension methods, that's not possible in Java, so it is the API maintainer's burden to implement default methods.
>
> Also, you're not alone. Stream has Stream.collect(supplier, accumulator, combiner), which is convenience for many cases of Stream.collect(Collector).
>
> JDK 9's Optional has Optional.stream(), which really wasn't "necessary", but so cool as it allows for flatmapping Stream<Optional<T>> to Stream<T> through
>
> stream.flatMap(Optional::stream)
>
> instead of
>
> stream.filter(Optional::isPresent).map(Optional::get)
>
> Just two examples. There are many other convenience APIs in the JDK, even trivial ones like the ArrayList constructors. The only one that is strictly necessary is the one that accepts a capacity. The others are there for convenience and improved performance.
>
> Should we add executeAsTransaction? What about other convenience methods? Suggestions welcome.
>
> Well, since you've added collect(Collector) (still very excited about that), how about also adding collect(supplier, accumulator, combiner) as a convenience?
>
> If you're positive that you're moving forward with convenience methods, I'd be happy to look for more opportunities to contribute both in ADBA and in JDBC (if that's something you might consider)
>
> Thanks,
> Lukas
>
> 2017-11-16 17:25 GMT+01:00 Douglas Surber <douglas.surber at oracle.com <mailto:douglas.surber at oracle.com>>:
> ADBA includes a fair number of convenience methods. These are methods that encapsulate common code patterns into default methods. DataSource.getConnection() is an obvious example.
>
> As default methods these are all implemented by calling other interface methods. Clearly they aren’t necessary as the app can make the same sequence of calls. On the other hand their use will result in more compact and more readable code. Also, some of them handle some not so obvious behavior so that the programmer doesn’t have to think about it in the common use case. Again DataSource.getConnection is a good example; look at Connection.connect. A third benefit and honestly the original motivation for many of them is as documentation, how to use the API.
>
> As a general notion, do these methods carry their weight? Are the benefits they provide worth whatever additional complexity they add?
>
> Here is a concrete example to consider. This is a method in Connection.
>
> public default CompletionStage<TransactionOutcome>
> executeAsTransaction(BiConsumer<OperationGroup, Transaction> action) {
> OperationGroup grp = this.operationGroup();
> Transaction tran = this.transaction();
> action.accept(grp, tran);
> grp.submit();
> return this.commitMaybeRollback(tran); *
> }
>
> Clearly this method is not strictly necessary. It is implemented entirely by calling other methods present in ADBA. Any application or framework can do exactly the same. On the other hand most applications and frameworks would make exactly the same sequence of calls so including this pattern in ADBA would be convenient. Finally, this provides an example of how to use ADBA to execute a transaction. This is actually the original motivation for creating this method.
>
> ADBA is based on some relatively primitive concepts. In some cases there is quite a conceptual distance between those primitive concepts and what an application wants to do. It is easier to specify, understand, and implement just the primitive concepts but that leaves a conceptual gap to what the app needs; how to compose the primitives into useful actions. The various convenience methods bridge this gap without adding any specification or implementation complexity. IMO these convenience methods actually reduce the complexity by adding an executable specification of how the primitives work together to support a higher level abstraction.
>
> Should we add executeAsTransaction? What about other convenience methods? Suggestions welcome.
>
> Douglas
>
>
> * Note: commitMaybeRollback has been corrected to return CompletionStage<TransactionOutcome> which is clearly what it should do.
>
>
>
More information about the jdbc-spec-discuss
mailing list