В 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 вызывали проблемы при обходе файловой системы . Если вы не напишите свою собственную утилиту, вам придется прибегнуть к этой красоте:
01
02
03
04
05
06
07
08
09
10
|
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
При написании этой серии блогов по Java 8 мы постоянно сталкивались с необходимостью заключать проверенные исключения в лямбда-выражения . И что мы, гики, делаем, когда часто сталкиваемся с проблемой? Мы это исправим! И мы создали jOOλ (также jOOL, jOO-Lambda) , лицензированный по ASL 2.0 , в котором мы почти полностью продублировали каждый FunctionalInterface
, доступный в JDK для поддержки проверенных исключений. Вот как бы вы использовали jOOλ в приведенном выше примере:
1
2
3
4
5
6
7
|
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. Если вы действительно хотите обрабатывать их, вы можете передать лямбда-обработчик исключений:
01
02
03
04
05
06
07
08
09
10
11
|
Arrays.stream(dir.listFiles()) .forEach(Unchecked.consumer( file -> { System.out.println(file.getCanonicalPath()); }, e -> { log.info( "Log stuff here" , e); throw new MyRuntimeException(e); } ); |
Второй пример теперь кажется столь же многословным, но не волнуйтесь. Вы, вероятно, повторно используете этот обработчик исключений и вернетесь к этому:
1
2
3
4
5
6
7
|
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
. Вот как вы готовитесь:
01
02
03
04
05
06
07
08
09
10
|
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
как таковой:
1
2
3
4
5
6
7
|
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()
:
01
02
03
04
05
06
07
08
09
10
|
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.
Жаль, что приведенный выше код не попал в JDK 8, поскольку это было бы шансом наконец-то значительно улучшить API JDBC. Другая, похожая попытка улучшить ситуацию была предпринята Джулианом Эксенбергером.
Java 8 альтернативы написания SQL
Мы также опубликовали несколько альтернатив jOOλ, используя Java 8 с SQL здесь: http://www.jooq.org/java-8-and-sql
Вывод
В то время как лямбда-выражения в Java 8 потрясающие, новый API Streams довольно неполон. При реализации вышесказанного нам пришлось реализовать наш собственный ResultSetIterator
и написать весь этот беспорядок, чтобы обернуть итератор в Stream
:
1
2
3
4
5
6
7
8
9
|
StreamSupport.stream( Spliterators.spliteratorUnknownSize( new ResultSetIterator<>( supplier, rowFunction, exceptionTranslator ), 0 ), false ); |
И вообще не нужно писать Iterator
, если бы мы только могли генерировать конечные потоки :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
// 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(); } ); |
Хотя jOOλ является приемлемым промежуточным решением, и ребята из Guava , вероятно, уже разрабатывают, как исправить свою библиотеку, на самом деле очень плохо, что в Java 8 нет такой функциональности утилиты.
Но мы жалуемся на высоком уровне. На следующей неделе, как и было обещано, мы увидим несколько примеров, связанных с параллелизмом, так что следите за обновлениями!
Подробнее о Java 8
А пока взгляните на удивительную страницу ресурсов Java 8 от Eugen Paraschiv.