Java Persistence Query Language

Query-buttonВ можно загружать сущности из базы по их id или по их типу. В первом случае загружается какая-то конкретная сущность, во втором — все сущности указанного типа.

В принципе с этим уже можно работать — загружаешь все сущности в память да обрабатываешь их с помощью Stream api. Разумеется в реальности никто так делать не будет: памяти на всех не хватит, загружать все сущности долго, обрабатываться они будут медленно и вообще моветон. Было бы гораздо лучше, если бы можно было загружать только нужные сущности и желательно бы это делать в стиле, описывая декларативно, что надо загрузить.

Java Persistence Query Language

Аналогом SQL в мире JPA является — Java Persistence Query Language. Фактически это как SQL, только запросы делаются не к таблицам, а к классам. Самый простой пример использования , это загрузка всех сущностей определённого типа, как я уже показывал раньше:

"from Person" буквально означает «дай-ка мне сущностей класса Person без каких-либо ограничений». Если же хочется, можно и уточнить, что нужна не Person сущность, а её поля:

В данном примере я говорю JPA «А дай-ка мне все поля passport из сущностей класса Person без каких-либо ограничений» и в ответ мне возвращаются именно сущности Passport, а не Person, даже не смотря на "from Person".

Методу createQuery() можно передавать опциональный тип возвращаемого объекта. Независимо от того, передали тип объектаили нет, JPA в любом случае самостоятельно определяет, что фактически возвращается из базы, а передаваемый тип используется только для строгой типизации во время компиляции. Например, если я в предудыщем примере заменю Passport.class на Person.class, компиляция пройдёт успешно, коллекция, возвращаемая getResultList(), будет иметь тип List<Person>, но при исполнении всё сломается:

Условия

Запросы вида «выбери мне свойство» прикольные, но бесполезные. Полезнее было бы по этим свойствам фильтровать:

Этот запрос означает «найди мне все сущности класса Passport, в поле owner которых хранятся сущности, поле lastName которых равно Testoff». Практически как обычный SQL, только в условиях пишутся не имена столбцов в таблицах, а имена полей в классах.

Можно использовать и обычные SQL выражения, такие как is null, between, like и так далее:

Параметры в запросах поддерживаются автоматически и не требуют особого обращения, в отличие от jdbc. Достаточно написать имя параметра, предварённое двоеточием, и задать его значение методом setParameter(). Помимо именованных параметров поддерживаются и позиционные:

В запросе выше показывается, как обращаться с связанным сущностями, используя оператор IN, представляющий хранящуюся в сущности коллекцию как элемент, к которому можно обратиться.

Именованные запросы

В JPA, помимо параметризованных запросов предусмотрели и статические именованные запросы, которые задаются отдельно, обычно поближе к сущности:

Именованные запросы позволяют повторно использовать один и тот же запрос в разных местах кода, тем самым уменьшая количество повторений в программе и упрощая его редактирование в будущем.  Кроме того, отделение кода запроса от места его исполнения позволяет последнему не заботиться вовсе о том, какой запрос фактически исполняется, уменьшая тем самым связность кода.

Код примера доступен на github.