Все больше и больше людей следят за последними обновлениями нашей платформы, внедряя функциональное программирование и для своих предприятий.
В Data Geekery мы используем Java 8 для наших интеграционных тестов jOOQ , поскольку использование нового API-интерфейса Streams с лямбда-выражениями значительно упрощает создание специальных тестовых данных.
Однако мы не чувствуем, что JDK предлагает столько, сколько могло бы , поэтому мы также внедрили и открыли jOOλ , небольшую служебную библиотеку, которая исправляет эти недостатки.
Обратите внимание, что мы не ставим целью заменить более сложные библиотеки, такие как functionsjava . jOOλ действительно исправляет недостатки.
Положить лямбды для работы с jOOλ или jOOQ
Недавно я столкнулся с этим вопросом переполнения стека , в котором запрашивалась потоковая передача набора результатов со всеми столбцами в один список. Например:
вход
1
2
3
4
5
6
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >+----+------------+------------+</span> + ---- + ------------ + ------------ +</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >|</span> |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ID |</span> ID |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >FIRST_NAME |</span> FIRST_NAME |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >LAST_NAME |</span> LAST_NAME |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >+----+------------+------------+</span> + ---- + ------------ + ------------ +</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >|</span> |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > 1 |</span> 1 |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Joslyn |</span> Джослин |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Vanderford |</span> Вандерфорд |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >|</span> |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > 2 |</span> 2 |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Rudolf |</span> Рудольф |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Hux |</span> Хакс |</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >+----+------------+------------+</span> + ---- + ------------ + ------------ +</span> |
Выход
1
2
3
4
5
6
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > 1 </span> 1 </span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Joslyn</span> Джослин</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Vanderford</span> Vanderford</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > 2 </span> 2 </span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Rudolf</span> Рудольф</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Hux</span> Hux</span> |
Это типичный пример учебника для использования функционального программирования, а не итеративного решения:
Итеративное решение
01
02
03
04
05
06
07
08
09
10
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ResultSet rs = ...;</span> ResultSet rs = ...;</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ResultSetMetaData meta = rs.getMetaData();</span> ResultSetMetaData meta = rs.getMetaData ();</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >List<Object> list = new ArrayList<>();</span> List <Object> list = new ArrayList <> ();</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > while (rs.next()) {</span> while (rs.next ()) {</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > for ( int i = 0 ; i < meta.getColumnCount(); i++) {</span> for ( int i = 0 ; i <meta.getColumnCount (); i ++) {</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >list.add(rs.getObject(i + 1 ));</span> list.add (rs.getObject (i + 1 ));</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >}</span> }</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >}</span> }</span> |
На самом деле, итеративное решение не так уж и плохо, но давайте узнаем, как это можно сделать с помощью функционального программирования.
Использование jOOλ
Мы используем jOOλ для этого примера по нескольким причинам:
- JDBC действительно не принял новые функции. Нет простого преобразования
ResultSet
вStream
, даже если оно должно быть. - К сожалению, новые функциональные интерфейсы не позволяют создавать проверенные исключения.
try .. catch
блоки внутри лямбды не совсем красиво - Интересно, что невозможно создать конечный поток, не реализовав
Iterator
илиSpliterator
Итак, вот простой код:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ResultSet rs = ...;</span> ResultSet rs = ...;</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ResultSetMetaData meta = rs.getMetaData();</span> ResultSetMetaData meta = rs.getMetaData ();</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >List<Object> list =</span> Список <Объект> список =</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Seq.generate()</span> Seq.generate ()</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.limitWhile(Unchecked.predicate(v -> rs.next()))</span> .limitWhile (Unchecked.predicate (v -> rs.next ()))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.flatMap(Unchecked.function(v -> IntStream</span> .flatMap (Unchecked.function (v -> IntStream)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.range( 0 , meta.getColumnCount())</span> .range ( 0 , meta.getColumnCount ())</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.mapToObj(Unchecked.intFunction(i -></span> .mapToObj (Unchecked.intFunction (i -></span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >rs.getObject(i + 1 )</span> rs.getObject (i + 1 )</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >))</span> ))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >))</span> ))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.toList()</span> .к списку()</span> |
Пока что это выглядит как многословно (или немного больше), чем итеративное решение. Как видите, здесь потребовалось несколько расширений jOOλ:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // This generate is a shortcut to generate an</span> // Это генерирует ярлык для генерации</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // infinite stream with unspecified content</span> // бесконечный поток с неопределенным содержимым</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Seq.generate()</span> Seq.generate ()</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // This predicate-based stream termination</span> // Это основанное на предикате завершение потока</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // unfortunately doesn't exist in the JDK</span> // к сожалению, не существует в JDK</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // Besides, the checked exception is wrapped in a</span> // Кроме того, проверенное исключение помещается в</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // RuntimeException by calling Unchecked.wrapper(...)</span> // RuntimeException путем вызова Unchecked.wrapper (...)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.limitWhile(Unchecked.predicate(v -> rs.next()))</span> .limitWhile (Unchecked.predicate (v -> rs.next ()))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // Standard JDK flatmapping, producing a "nested"</span> // Стандартное отображение JDK, производящее "вложенный"</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // stream of column values for the "outer" stream</span> // поток значений столбца для "внешнего" потока</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // of database rows</span> // строк базы данных</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.flatMap(Unchecked.function(v -> IntStream</span> .flatMap (Unchecked.function (v -> IntStream)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.range( 0 , meta.getColumnCount())</span> .range ( 0 , meta.getColumnCount ())</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.mapToObj(Unchecked.intFunction(i -></span> .mapToObj (Unchecked.intFunction (i -></span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >rs.getObject(i + 1 )</span> rs.getObject (i + 1 )</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >))</span> ))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >))</span> ))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // This is another convenience method that is more</span> // Это еще один удобный метод, который более</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" > // verbose to write with standard JDK code</span> // подробно писать со стандартным кодом JDK</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.toList()</span> .к списку()</span> |
Используя jOOQ
У jOOQ есть еще более удобный API для работы с записями результатов вашего оператора SQL. Рассмотрим следующую часть логики:
1
2
3
4
5
6
7
8
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ResultSet rs = ...;</span> ResultSet rs = ...;</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >List<Object> list =</span> Список <Объект> список =</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >DSL.using(connection)</span> DSL.using (соединение)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.fetch(rs)</span> .fetch (RS)</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.stream()</span> .ручей()</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.flatMap(r -> Arrays.stream(r.intoArray()))</span> .flatMap (r -> Arrays.stream (r.intoArray ()))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.collect(Collectors.toList());</span> .collect (Collectors.toList ());</span> |
Обратите внимание, что в приведенном выше примере используется стандартный API JDK, не прибегая к jOOλ для удобства. Если вы хотите использовать jOOλ с jOOQ, вы можете написать:
1
2
3
4
5
6
|
<span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >ResultSet rs = ...;</span> ResultSet rs = ...;</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >List<Object> list =</span> Список <Объект> список =</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >Seq.seq(DSL.using(connection).fetch(rs))</span> Seq.seq (DSL.using (подключение) .fetch (RS))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.flatMap(r -> Arrays.stream(r.intoArray()))</span> .flatMap (r -> Arrays.stream (r.intoArray ()))</span> <span class = "notranslate" onmouseover= "_tipon(this)" onmouseout= "_tipoff()" ><span class = "google-src-text" style= "direction: ltr; text-align: left" >.toList();</span> .к списку();</span> |
Легко? Я бы так сказал! Давайте вспомним, что этот пример:
- Извлекает ResultSet JDBC в коллекцию Java
- Преобразует каждую запись в наборе результатов в массив значений столбцов
- Преобразует каждый массив в поток
- Сглаживает этот поток в поток потоков
- Собирает все значения в один список
Уф!
Вывод
Мы приближаемся к захватывающим временам! Это займет некоторое время, пока все идиомы Java 8 и функциональное мышление не станут «естественными» для разработчиков Java, в том числе и на предприятии.
Идея иметь своего рода источник данных, который может быть сконфигурирован с конвейерными преобразованиями данных, выраженными в виде лямбда-выражений, которые лениво оцениваются, очень убедительна. jOOQ — это API, который инкапсулирует источники данных SQL очень бегло и интуитивно, но на этом он не останавливается. jOOQ создает регулярные коллекции записей JDK, которые можно трансформировать «из коробки» с помощью нового API потоков.
Мы верим, что это кардинально изменит то, как экосистема Java будет думать о преобразовании данных . Следите за новыми примерами в этом блоге !
Ссылка: | Не пропустите возможность написания Java 8 SQL One-Liners с jOOλ или jOOQ от нашего партнера по JCG Лукаса Эдера в блоге JAVA, SQL и AND JOOQ . |