В 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 было несколько вопросов о переполнении стека, связанных с этой темой.
- Обязательная проверка исключений в лямбда-выражениях для стандартных функциональных интерфейсов 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! });
Мы считаем, что можно с уверенностью сказать:
Обходной путь должен написать свой собственный, CheckedConsumer
который оборачивает проверенное исключение. Такой потребитель будет многократно использоваться, но … Вы думали обо всем остальном FunctionalInterfaces
? В java.util.function
упаковке их довольно много :
jOOλ — Исправление лямбды в Java 8
При написании этой серии блогов по 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 );
Другие также доступны.
Это здорово, не правда ли?
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