Введение
В области программирования на Java, особенно в контексте сохранения данных и манипулирования ими с использованием Java Persistence Query Language (далее JPQL), разработчики сталкиваются с выбором между двумя основными подходами к формулированию запросов: JPQL и Criteria API [1]. Хотя JPQL был традиционным выбором для многих, его ограничения, особенно с точки зрения безопасности типов и склонности к ошибкам во время выполнения, привели к растущему интересу к Criteria API и Metamodel.
Criteria API выделяется как предопределённый набор инструментов для конструирования запросов к сущностям, функционирующий как альтернативный механизм к JPQL. Особенностью Criteria API является его типобезопасность, что позволяет обнаруживать ошибки на этапе компиляции, в отличие от JPQL, где ошибки часто выявляются только во время выполнения. Это обеспечивает более высокий уровень безопасности и удобства при разработке. Кроме того, Criteria API обладает гибкостью в модификации запросов за счет изменения их синтаксиса, а также способностью к портированию между различными базами данных.
В свою очередь при использовании метамодели для определения условий запроса, например, при группировке возвращаемых объектов по определённым атрибутам, Criteria API предоставляет метод having, который ограничивает результаты запроса в соответствии с заданными условиями. Это демонстрирует гибкость API в создании сложных запросов и обработке данных [2, 3].
Рис. 1. Принцип работы SQL функций в JPQL или Criteria API
Целью этой статьи является анализ основных проблем, с которыми сталкиваются разработчики при выборе между JPQL и Criteria API для формулирования запросов к данным.
1. JPA Criteria API
JPA Criteria API – представляет собой современный программный интерфейс, разработанный для формирования запросов к сущностям баз данных, который аналогичен Java Persistence Query Language (JPQL), но отличается надежной интеграцией с парадигмами объектно-ориентированного программирования. Эта интеграция позволяет извлекать данные из баз данных с использованием объектов и методов, предоставляя разработчикам инструменты для создания запросов, которые не только интуитивно понятны и гибки, но и полностью соответствуют принципам объектно-ориентированного программирования. Criteria API также поддерживает операции CRUD (создание, чтение, обновление, удаление), что позволяет разработчикам выполнять широкий спектр манипуляций с данными, помимо простого извлечения данных [2]. В результате Criteria API становится высокоэффективным решением для реализации динамических запросов, предлагая разработчикам мощный и универсальный инструмент для объектно-ориентированной обработки данных.
С точки зрения производительности и эффективности, запросы, созданные с помощью JPQL и JPA Criteria API, демонстрируют идентичные показатели. Однако, для реализации простых статических запросов часто предпочтительнее использовать JPQL из-за его прямолинейности (например, через именованные запросы). В контексте динамически формируемых запросов, особенно когда конечный запрос зависит от пользовательского ввода, содержащего множество необязательных полей, JPA Criteria API представляет собой более подходящий выбор благодаря своей способности упростить и уточнить процесс создания запроса, минимизируя необходимость в сложных операциях конкатенации строк.
Процесс работы с JPA Criteria API включает в себя последовательность шагов, начиная с создания экземпляра EntityManager и открытия транзакции. Этот шаг также зависит от контекста приложения (например, Java SE или Java EE/Jakarta EE). В простом Java SE приложении это может выглядеть следующим образом.
Далее, разработчик должен создать объект CriteriaBuilder, который используется для создания критериев запросов для поиска или обновления сущностей.
С помощью CriteriaBuilder формируется объект CriteriaQuery, который используется для формирования запросов к базе данных. Здесь указывается тип возвращаемых данных.
Затем создается корневой объект (root), который указывает на таблицу (или сущность), относительно которой будет производиться выборка.
Если необходимо наложить ограничения (например, фильтры по определенным полям), можно это сделать с помощью CriteriaBuilder. В данном примере ограничений нет, так как цель – получить все записи.
Здесь указывается, что необходимо выбрать (в данном случае, все записи Person).
После формирования запроса его нужно выполнить с помощью EntityManager. Результат можно получить в виде списка.
В конце работы с базой данных необходимо закрыть транзакцию и освободить ресурсы.
Важно отметить, что каждый из указанных выше шагов является необходимым для корректного формирования запроса через JPA Criteria API, где корневой объект определяет источник данных в соответствии с JPQL, а CriteriaQuery указывает на тип возвращаемых результатов [2].
2. Генерация классов метамодели JPA
В рамках процесса разработки, одним из ключевых этапов является генерация классов метамодели, для чего предпочтительно применять специализированные инструменты. В данном контексте JBoss выделяется как один из инструментальных средств, предлагаемых для выполнения данной задачи, хотя и не является единственным вариантом. Альтернативы JBoss включают в себя такие решения, как EclipseLink, OpenJPA, и DataNucleus, каждое из которых подходит для создания метамодели.
Для интеграции инструмента генерации метамодели от JBoss в проект, необходимо выполнить дополнение файла pom.xml проекта соответствующей зависимостью. Это позволит автоматизировать процесс генерации классов метамодели в рамках существующей инфраструктуры Maven. Активация данной операции произойдет автоматически при выполнении стандартной команды сборки проекта через Maven, обеспечивая тем самым эффективную интеграцию генерации метамодели в процесс разработки.
Рис. 2. Пример генерации классов метамодели на JPA
3. Статические классы метамодели JPA
В соответствии с нормами, установленными спецификацией JPA, процесс генерации класса метамодели предусматривает его размещение в идентичном пакете, где находится исходный класс сущности. Дополнительно, наименование сгенерированного класса метамодели получает суффикс в виде символа подчеркивания "_", расположенного после оригинального имени класса сущности. Таким образом, для класса сущности с наименованием Student будет сформирован класс метамодели под наименованием Student, структура которого будет ориентирована на представление и описание характеристик исходного класса сущности.
Рис. 3. Пример статистических классов метамодели JPA
4. Использование классов метамодели JPA
Можем использовать классы статической метамодели так же, как использовали бы строковые ссылки на атрибуты. API запросов критериев предоставляет перегруженные методы, которые принимают ссылки на строку, а также реализации интерфейса атрибута. Классы метамодели, соответствующие классам сущностей, имеют следующий тип: javax.persistence.metamodel.EntityType.
Классы метамоделей обычно создаются с использованием процессоров аннотаций на этапе разработки или исполнения программы. Этот подход позволяет разработчикам приложений, применяющим критериальные запросы, автоматизировать генерацию статических классов метамодели при помощи процессора аннотаций, предоставляемого поставщиком сервиса сохранения данных. Альтернативно, доступ к классу метамодели может быть получен путем вызова метода getModel для корневого элемента запроса. В дополнение, возможно сначала получить экземпляр интерфейса метамодели, а затем использовать метод entity этого экземпляра для передачи типа сущности. Эти механизмы предоставляют гибкие пути для работы с метаданными сущностей, что способствует повышению эффективности разработки и взаимодействия с базой данных.
5. Использование Criterions API и Metamodel API для создания базовых типобезопасных запросов.
Фундаментальная структура запроса Criterium организуется вокруг трех основных компонентов: предложения SELECT, предложения FROM и, по желанию, предложения WHERE, что напоминает конструкцию запроса в JPQL. Отличительной особенностью запросов Criterions является их формирование через объекты Java, что обеспечивает создание типобезопасных запросов. Это подход позволяет интегрировать динамическое построение запросов в программный код, придавая процессу гибкость и уменьшая вероятность ошибок, связанных с несоответствием типов данных.
Чтобы получить экземпляр интерфейса CriteriaBuilder, вызовите метод getCriteriaBuilder либо в экземпляре EntityManager, либо в экземпляре EntityManagerFactory. Данный интерфейс играет центральную роль в конструировании выражений для запросов в рамках Criteria API, предлагая обширный набор методов для формирования условий. Эти методы охватывают широкий спектр операций, включая арифметические операции, операции со строками, датами, временем, логическими выражениями, а также соответствуют функциям, присутствующим в JPQL.
Таким образом, CriteriaBuilder обеспечивает возможность создавать комплексные выражения, которые могут включать условия сравнения, логические операции, операции над строками и датами, позволяя тем самым разработчикам формулировать детализированные и точные запросы к базе данных. В частности, условные методы, доступные через CriteriaBuilder, позволяют реализовать логику условного выбора, аналогичную той, которая используется в JPQL, но с преимуществами типобезопасности и интеграции с объектно-ориентированным программированием [5].
Таблица
Условные методы в интерфейсе CriteriaBuilder [5]
Условный метод | Описание |
equal | Проверяет, равны ли два выражения |
notEqual | Проверяет, не равны ли два выражения |
gt | Проверяет, является ли первое числовое выражение большим, чем второе числовое выражение |
ge | Проверяет, является ли первое числовое выражение большим или равным второму числовому выражению |
lt | Проверяет, является ли первое числовое выражение меньшим, чем второе числовое выражение |
le | Проверяет, является ли первое числовое выражение меньше или равно второму числовому выражению |
between | Проверяет, находится ли первое выражение между вторым и третьим выражениями по значению |
like | Проверяет, соответствует ли выражение заданному шаблону |
Заключение
Переход с JPQL на Criteria API и метамодель в Hibernate ORM представляет собой значительный прогресс в способах взаимодействия Java-приложений с базами данных. В этой статье рассмотрены присущие JPQL ограничения, такие как отсутствие безопасности типов и повышенный риск ошибок во время выполнения, а также представлены Criteria API и Metamodel в качестве превосходных альтернатив. Благодаря подробному сравнению и практическим примерам мы увидели, как Criteria API обеспечивает большую гибкость, безопасность типов и простоту модификации запросов, в то время как Metamodel повышает точность и удобство сопровождения запросов. Хотя переход требует тщательного понимания нового API и первоначальных инвестиций в рефакторинг существующего кода, долгосрочные преимущества включают повышение производительности приложений, надежности и продуктивности разработчиков. В конечном счете, это исследование выступает за внедрение Criteria API и метамодели в Hibernate ORM как средства обеспечения более безопасных, поддерживаемых и эффективных операций с базами данных в приложениях на базе Java.