Несколько дней назад я прочитал интересное интервью с Ming-Yee Iu о JINQ . JINQ, как видно из названия, является попыткой предоставить что-то похожее на LINQ для Java. Основная идея заключается в том, чтобы закрыть семантический разрыв между объектно-ориентированным кодом, который выполняет запросы в реляционной модели данных. Запросы для модели реляционной базы данных должны быть легко интегрированы в код таким образом, чтобы он казался более естественным.
Исследование LINQ пришло к выводу, что алгоритмы, преобразующие код в запросы реляционной базы данных, лучше всего работают с функциональным кодом. Поскольку Java 8 поставляется с потоковым API, автор использует его для реализации идей своего доктора философии в Java.
Чтобы запачкать руки, мы начнем с простого проекта, который использует Hibernate поверх JPA вместе с базой данных H2 и JINQ:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
< dependencies > < dependency > < groupId >javax</ groupId > < artifactId >javaee-api</ artifactId > < version >${jee.version}</ version > < scope >provided</ scope > </ dependency > < dependency > < groupId >com.h2database</ groupId > < artifactId >h2</ artifactId > < version >${h2.version}</ version > </ dependency > < dependency > < groupId >org.hibernate</ groupId > < artifactId >hibernate-entitymanager</ artifactId > < version >${hibernate.version}</ version > </ dependency > < dependency > < groupId >org.jinq</ groupId > < artifactId >jinq-jpa</ artifactId > < version >1.8.10</ version > </ dependency > </ dependencies > |
Чтобы использовать потоки JINQ, мы должны создать провайдера, который получает EntityManagerFactory
качестве аргумента:
1
2
|
EntityManagerFactory factory = Persistence.createEntityManagerFactory( "PersistenceUnit" ); JinqJPAStreamProvider streams = new JinqJPAStreamProvider(factory); |
Вставив несколько человек в нашу базу данных, мы можем легко запросить их:
1
2
3
4
|
List<String> firstNames = streams.streamAll(entityManager, Person. class ) .map(Person::getFirstName) .collect(toList()); firstNames.forEach(System.out::println); |
Использование метода streamAll()
ранее созданного JinqJPAStreamProvider
дает нам доступ ко всем лицам в базе данных. В этом простом примере мы хотим вывести только имя каждого человека; следовательно, мы map
список и collect
все результаты в List
. Этот список печатается с использованием метода forEach()
и ссылки на метод println()
.
Взглянув на сгенерированный код SQL, мы видим, что все столбцы выбраны:
1
2
3
4
5
6
7
|
select person0_.id as id1_4_, person0_.FIRST_NAME as FIRST_NA2_4_, person0_.ID_CARD_ID as ID_CARD_4_4_, person0_.LAST_NAME as LAST_NAM3_4_, from T_PERSON person0_ |
Конечно, мы можем уточнить оператор, используя метод select()
:
1
2
3
4
5
|
List<String> firstNames = streams.streamAll(entityManager, Person. class ) .select(Person::getFirstName) .where(p -> p.equals( "Homer" )) .collect(toList()); firstNames.forEach(System.out::println); |
Кроме того, мы также добавили предикат ( where firstName = 'Homer'
):
1
2
3
4
5
6
|
select person0_.FIRST_NAME as FIRST_NA2_4_ from T_PERSON person0_ where person0_.FIRST_NAME= 'Homer' |
Оставляя этот простой пример, мы теперь хотим создать запрос, который выбирает всех вундеркиндов с именем «Christian», которые работают во временном и материальном проектах:
1
2
3
4
5
6
7
|
List<String> geeks = streams.streamAll(entityManager, Project. class ) .where(p -> p.getProjectType() == Project.ProjectType.TIME_AND_MATERIAL) .joinList(Project::getGeeks) .where(g -> g.getTwo().getFirstName().equals( "Christian" )) .map(p -> p.getTwo().getFirstName()) .collect(toList()); geeks.forEach(System.out::println); |
Как видно из приведенного выше кода, мы используем первое предложение where()
для выбора всех временных и материальных проектов. joinList()
присоединяется к таблице geek, в то время как последующее предложение where()
также ограничивается выбором только вундеркиндов с именем «Christian». Et voila, это созданный SQL-запрос:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
select geek2_.FIRST_NAME as col_0_0_ from T_PROJECT project0_ inner join T_GEEK_PROJECT geeks1_ on project0_.id=geeks1_.PROJECT_ID inner join T_GEEK geek2_ on geeks1_.GEEK_ID=geek2_.id where project0_.projectType= 'TIME_AND_MATERIAL' and geek2_.FIRST_NAME= 'Christian' limit ? |
Заключение . Поработав с API критериев JPA некоторое время назад, я должен сказать, что первые шаги с JINQ более интуитивны и их легче записать. JINQ действительно помогает сократить разрыв между миром реляционных баз данных, используя потоки в Java 8.
Ссылка: | Использование JINQ с JPA и H2 от нашего партнера по JCG Мартина Моиса в блоге Martin’s Developer World . |