Несколько дней назад я прочитал интересное интервью с 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 . |