Статьи

Пятничные вкусности Java 8: потоки SQL ResultSet

В  Data Geekery мы любим Java. И так как мы действительно  входим в свободный API jOOQ и запросы DSL , мы абсолютно взволнованы тем, что Java 8 принесет в нашу экосистему. Мы  пару раз писали о приятных вкусностях Java 8 , и теперь мы чувствуем, что пришло время начать новую серию блогов,…

Ява 8 Пятница

Каждую пятницу мы показываем вам пару замечательных новых функций Java 8 в виде учебника, которые используют лямбда-выражения, методы расширения и другие замечательные вещи. Вы найдете исходный код на GitHub .

Java 8 Goodie: SQL ResultSet Streams

Да, тема SQL должна быть рассмотрена снова. Даже если на  прошлой неделе мы пообещали статью о параллелизме , есть один очень важный аспект лямбда-кода в Java 8 и совместимость с «устаревшими» API, о которых нам нужно поговорить, во-первых.

Проверенные исключения

Да. К сожалению, эти звери из прошлого все еще преследуют нас, больше, чем когда-либо, когда мы используем лямбда-выражения Java 8. Еще до выпуска Java 8 было несколько вопросов о переполнении стека, связанных с этой темой.

Давайте вспомним, как исключения IOException вызывали проблемы при обходе файловой системы . Если вы не напишите свою собственную утилиту, вам придется прибегнуть к этой красоте:

Arrays.stream(dir.listFiles()).forEach(file -> {
    try {
        System.out.println(file.getCanonicalPath());
    }
    catch (IOException e) {
        throw new RuntimeException(e);
    }
    // Ouch, my fingers hurt! All this typing!
});

Мы считаем, что можно с уверенностью сказать:

Java 8 и проверенные исключения не совпадают.чирикать это

Обходной путь должен написать свой собственный,  CheckedConsumer который оборачивает проверенное исключение. Такой потребитель будет многократно использоваться, но … Вы думали обо всем остальном  FunctionalInterfaces? В java.util.function упаковке их довольно много  :

jOOλ — Исправление лямбды в Java 8

jool-логотип-черныйПри написании  этой серии блогов по Java 8 мы постоянно сталкивались с необходимостью заключать проверенные исключения в лямбда-выражения . И что мы, гики, делаем, когда часто сталкиваемся с проблемой? Мы это исправим! И мы создали  jOOλ (также jOOL, jOO-Lambda)лицензированный ASL 2.0 , где мы дублировали почти все,  FunctionalInterface что доступно в JDK для поддержки проверенных исключений. Вот как бы вы использовали jOOλ в приведенном выше примере:

Arrays.stream(dir.listFiles()).forEach(
    Unchecked.consumer(file -> {
        // Throw all sorts of checked exceptions
        // here, we don't care...
        System.out.println(file.getCanonicalPath());
    })
);

Приведенный выше пример показывает, как вы можете просто игнорировать и передавать проверенные исключения как RuntimeExceptions. Если вы действительно хотите обрабатывать их, вы можете передать лямбда-обработчик исключений:

Arrays.stream(dir.listFiles()).forEach(Unchecked.consumer(
    file -> {
        System.out.println(file.getCanonicalPath());
    },
    e -> {
        log.info("Log stuff here", e);
        throw new MyRuntimeException(e);
    }
);

Второй пример теперь кажется столь же многословным, но не волнуйтесь. Вы, вероятно, повторно используете этот обработчик исключений и вернетесь к этому:

Arrays.stream(dir.listFiles()).forEach(Unchecked.consumer(
    file -> {
        System.out.println(file.getCanonicalPath());
    },
    myExceptionHandler
);

jOOλ — Предоставление потоков результатов JDBC ResultSet

К сожалению, большинство усилий в Java 8 Streams API было сделано в области правильной реализации распараллеливаемых потоков. Хотя это очень полезно для тех из нас, кто на самом деле занимается параллельными вычислениями, для большинства других лучшая интеграция с устаревшими API-интерфейсами была бы лучше. Одним из API, который серьезно заслуживает некоторого поднятия, является  JDBC, и мы уже писали об этом раньше . С помощью  jOOλ теперь вы можете генерировать потоки непосредственно из ResultSets или даже из  PreparedStatements. Вот как вы готовитесь:

Class.forName("org.h2.Driver");
try (Connection c = getConnection()) {
    String sql = "select schema_name, is_default " +
                 "from information_schema.schemata " +
                 "order by schema_name";
    try (PreparedStatement stmt = c.prepareStatement(sql)) {
        // code here
    }
}

Теперь все, что вам нужно сделать при использовании  jOOλ,  — это транслировать ваши данные PreparedStatements как таковые:

SQL.stream(stmt, Unchecked.function(rs -> new SQLGoodies.Schema(
    rs.getString("SCHEMA_NAME"),
    rs.getBoolean("IS_DEFAULT"))
))
.forEach(System.out::println);

Где  SQLGoodies.Schema просто обычный POJO. Вот некоторые из stream() сигнатур метода:

public static <T> Stream<T> stream(
    PreparedStatement stmt,
    Function<ResultSet, T> rowFunction
);

public static <T> Stream<T> stream(
    PreparedStatement stmt,
    Function<ResultSet, T> rowFunction,
    Consumer<? super SQLException> exceptionHandler
);

Другие также доступны.

Это здорово, не правда ли?

JDBC ResultSets должен быть Java 8 Streams.чирикать это

Too bad, the above code didn’t make it into the JDK 8, as this would have been a chance to finally greatly improve on the JDBC API. Another, similar attempt at improving things has been done here by Julian Exenberger.

Java 8 alternatives of writing SQL

We’ve also published a couple of alternatives to jOOλ, using Java 8 with SQL here:

http://www.jooq.org/java-8-and-sql

Conclusion

While Java 8′s lambda expressions are awesome, the new Streams API is pretty incomplete. When implementing the above, we had to implement our own ResultSetIterator, and write all this mess to wrap the iterator in aStream:

StreamSupport.stream(Spliterators.spliteratorUnknownSize(
    new ResultSetIterator<>(
        supplier,
        rowFunction,
        exceptionTranslator
    ), 0
), false);

And it shouldn’t be necessary to write an Iterator in the first place, if onlywe were able to generate finite streams:

// Unfortunately, this method doesn't exist
Stream.generate(
    // Supplier, generating new POJOs
    () -> {
        rs.next();
        return new SQLGoodies.Schema(
            rs.getString("SCHEMA_NAME"),
            rs.getBoolean("IS_DEFAULT")
        );
    },

    // Predicate, terminating the Stream
    () -> { !rs.isLast(); }
);

While jOOλ is an acceptable intermediate solution, and the Guava guys are probably already working out how to fix their library, it is really too bad, that Java 8 is lacking such utility functionality.

But we’re complaining on a high level. Next week, as promised, we’ll see a couple of examples related to concurrency, so stay tuned!

More on Java 8

In the mean time, have a look at Eugen Paraschiv’s awesome Java 8 resources page