Раньше каждую пятницу мы давали вам новую статью о том, что нового в Java 8. Это была очень захватывающая серия блогов , но мы хотели бы еще раз сосредоточиться на нашем основном контенте, который является Java и SQL , Время от времени мы будем вести блог о Java 8, но уже не каждую пятницу (как некоторые из вас уже заметили).
В этом последнем коротком посте серии Java 8 Friday, мы хотели бы еще раз повторить тот факт, что мы считаем, что будущее принадлежит функциональному преобразованию реляционных данных (в отличие от ORM) . Мы потратили около 20 лет, используя парадигму разработки объектно-ориентированного программного обеспечения . Многие из нас были очень догматичны в этом. Однако за последние 10 лет «новая» парадигма начала набирать обороты в сообществах программистов: функциональное программирование .
Функциональное программирование не что новое, однако. Лисп был очень ранним функциональным языком программирования. XSLT и SQL также несколько функциональны (и декларативны!). Поскольку мы большие поклонники функциональной (и декларативной!) Природы SQL, мы весьма взволнованы тем фактом, что теперь у нас есть сложные инструменты в Java для преобразования табличных данных, извлеченных из баз данных SQL. Streams!
SQL ResultSets очень похожи на потоки
Как мы уже указывали ранее, JDBC ResultSets и Java 8 Streams очень похожи . Это еще более верно, когда вы используете jOOQ, который заменяет JDBC ResultSet на a org.jooq.Result
, который расширяется java.util.List
и, таким образом, автоматически наследует все функциональные возможности потоков. Рассмотрим следующий запрос, который позволяет получить отношение «один ко многим» между записями BOOK и AUTHOR:
Map<Record2<String, String>, List<Record2<Integer, String>>> booksByAuthor = // This work is performed in the database // -------------------------------------- ctx.select( BOOK.ID, BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME ) .from(BOOK) .join(AUTHOR) .on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) .orderBy(BOOK.ID) .fetch() // This work is performed in Java memory // ------------------------------------- .stream() // Group BOOKs by AUTHOR .collect(groupingBy( // This is the grouping key r -> r.into(AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME), // This is the target data structure LinkedHashMap::new, // This is the value to be produced for each group: A list of BOOK mapping( r -> r.into(BOOK.ID, BOOK.TITLE), toList() ) ));
Свободное владение API Java 8 Streams очень идиоматично для тех, кто привык писать SQL с помощью jOOQ. Очевидно, что вы также можете использовать что-то отличное от jOOQ, например Spring JdbcTemplate или Apache Commons DbUtils, или просто обернуть JDBC ResultSet в итератор…
Что очень хорошо в этом подходе, по сравнению с ORM, так это то, что волшебства не происходит вообще. Каждая часть логики сопоставления является явной и, благодаря универсальным Java, полностью безопасна для типов. Тип booksByAuthor
выходных данных является сложным и немного сложным для чтения / записи, в этом примере, но он также полностью описательный и полезный.
То же функциональное преобразование с POJO
Если вы не слишком довольны использованием типов Record2
кортежей jOOQ , не проблема. Вы можете указать свои собственные объекты передачи данных следующим образом:
class Book { public int id; public String title; @Override public String toString() { ... } @Override public int hashCode() { ... } @Override public boolean equals(Object obj) { ... } } class Author { public String firstName; public String lastName; @Override public String toString() { ... } @Override public int hashCode() { ... } @Override public boolean equals(Object obj) { ... } }
С помощью вышеприведенного DTO теперь вы можете использовать встроенное отображение POJO в jOOQ для преобразования записей jOOQ в ваши собственные доменные классы:
Map<Author, List<Book>> booksByAuthor = ctx.select( BOOK.ID, BOOK.TITLE, AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME ) .from(BOOK) .join(AUTHOR) .on(BOOK.AUTHOR_ID.eq(AUTHOR.ID)) .orderBy(BOOK.ID) .fetch() .stream() .collect(groupingBy( // This is the grouping key r -> r.into(Author.class), LinkedHashMap::new, // This is the grouping value list mapping( r -> r.into(Book.class), toList() ) ));
Явность против неявности
В Data Geekery мы считаем, что для разработчиков Java наступило новое время. Время, когда Annotatiomania ™ (наконец-то!) Заканчивается, и люди перестают воспринимать все это неявное поведение посредством магии аннотаций. ORM зависят от огромного количества спецификаций, объясняющих, как каждая аннотация работает с аннотацией друг друга. Трудно перепроектировать (или отладить!) Этот не очень хорошо понятный язык аннотаций, который JPA принес нам.
С другой стороны, SQL довольно хорошо понят. Таблицы представляют собой простую в обращении структуру данных, и если вам нужно преобразовать эти таблицы в нечто более объектно-ориентированное или более иерархически структурированное, вы можете просто применить функции к этим таблицам и группировать значения самостоятельно! Явно группируя эти значения, вы сохраняете полный контроль над отображением, так же как и с jOOQ, вы полностью контролируете свой SQL.
Вот почему мы считаем, что в ближайшие 5 лет ORM потеряют свою актуальность, и люди снова начнут использовать явные , безгосударственные и безмасляные методы преобразования данных, используя потоки Java 8.