Автор оригинала: Vlad Mihalcea.
Вступление
При выполнении запроса сущности (например, JPQL, HQL или API критериев) вы можете использовать любую функцию SQL без необходимости ее регистрации, если функция передается непосредственно в предложение WHERE базового оператора SQL.
Однако, если функция SQL используется в предложении SELECT, и Hibernate не зарегистрировал функцию SQL (будь то функция, зависящая от базы данных или определяемая пользователем), вам придется зарегистрировать функцию перед ее использованием в запросе сущности.
В этой статье вы узнаете о различных способах регистрации функций SQL с помощью JPA и гибернации.
Проекция DTO с помощью GROUP_CONCAT
Как уже объяснялось в этой статье , прогнозы идеально подходят для отчетов и аналитики, и по этой причине мы используем следующее Опубликуйте резюме D В
bean для хранения каждой записи нашего отчета о публикации резюме:
Теперь, поскольку наше приложение работает на MySQL, мы можем использовать GROUP_CONCAT
для объединения нескольких строковых значений, принадлежащих одной и той же группе.
Запрос JPQL, который создает проекцию DTO, выглядит следующим образом:
ListpostSummaries = entityManager .createQuery(""" select p.id as id, p.title as title, group_concat(t.name) as tags from Post p left join p.tags t group by p.id, p.title """) .unwrap(Query.class) .setResultTransformer( Transformers.aliasToBean(PostSummaryDTO.class) ) .getResultList();
Или вместо этого мы могли бы использовать API критериев JPA:
CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuerycq = cb.createQuery( PostSummaryDTO.class ); Root post = cq.from(Post.class); Join tags = post.join("tags", JoinType.LEFT); cq.groupBy(post.get("id"), post.get("title")); cq.select( cb .construct( PostSummaryDTO.class, post.get("id"), post.get("title"), cb.function( "group_concat", String.class, tags.get("name") ) ) ); List postSummaries = entityManager .createQuery(cq) .getResultList();
Написание запросов API критериев JPA не очень просто. Плагин Codota IDE может помочь вам в написании таких запросов, что повысит вашу производительность.
Для получения более подробной информации о том, как вы можете использовать Codota для ускорения процесса написания запросов API критериев, ознакомьтесь с этой статьей .
Однако, если мы попытаемся выполнить следующий запрос JPQL или API критериев, по умолчанию Hibernate выдаст следующее Исключение QueryException
при анализе запроса JPQL:
java.lang.IllegalArgumentException: org.hibernate.QueryException: No data type for node: org.hibernate.hql.internal.ast.tree.MethodNode \-[METHOD_CALL] MethodNode: '(' +-[METHOD_NAME] IdentNode: 'group_concat' {originalText=group_concat} \-[EXPR_LIST] SqlNode: 'exprList' \-[DOT] DotNode: 'groupconca2_.name' {propertyName=name,dereferenceType=PRIMITIVE,getPropertyPath=name,path=t.name,tableAlias=groupconca2_,className=com.vladmihalcea.book.hpjp.hibernate.query.function.GroupConcatFunctionTest$Tag,classAlias=t} +-[ALIAS_REF] IdentNode: 'groupconca2_.id' {alias=t, className=com.vladmihalcea.book.hpjp.hibernate.query.function.GroupConcatFunctionTest$Tag, tableAlias=groupconca2_} \-[IDENT] IdentNode: 'name' {originalText=name} [select p.id as id, p.title as title, group_concat(t.name) as tags from com.vladmihalcea.book.hpjp.hibernate.query.function.GroupConcatFunctionTest$Post p left join p.tags t group by p.id, p.title]
Проблема в том, что Hibernate не распознает функцию group_concat
SQL, поэтому запрос JPQL не может быть проанализирован.
Теперь существует несколько способов регистрации такой функции SQL, и мы рассмотрим их все следующим образом.
В Hibernate 5 API критериев анализируется в JPQL, поэтому все, что относится к анализу запросов JPQL, также применимо к запросам API критериев.
Регистрация функции SQL с помощью диалекта Hibernate
Наиболее известный способ регистрации функции SQL-это пользовательский режим гибернации Диалект
.
public class CustomMySQLDialect extends MySQL57Dialect { public CustomMySQLDialect() { super(); registerFunction( "group_concat", new StandardSQLFunction( "group_concat", StandardBasicTypes.STRING ) ); } }
И использование Пользовательского MySQLDialect
при загрузке в спящий режим:
name="hibernate.dialect" value="com.vladmihalcea.book.hpjp.hibernate.query.function.CustomMySQLDialect"
Однако у этого метода есть большой недостаток. Каждый раз , когда нам нужно обновить Диалект
, мы должны помнить, что нужно изменить Пользовательский класс MySQLDialect
, чтобы расширить новый Диалект
. Таким образом, было бы гораздо удобнее, если бы мы могли просто зарегистрировать функцию SQL без необходимости переопределять классы гибернации.
Регистрация функции SQL с помощью JPA и конструктора метаданных.
Начиная с Hibernate 5.2.18, вы можете использовать утилиту Конструктор метаданных
для настройки Конструктор метаданных
, даже если вы загружаетесь через JPA.
Интерфейс Конструктор метаданных
может быть реализован следующим образом:
public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor { @Override public void contribute(MetadataBuilder metadataBuilder) { metadataBuilder.applySqlFunction( "group_concat", new StandardSQLFunction( "group_concat", StandardBasicTypes.STRING ) ); } }
И мы можем предоставить Конструктор метаданных функций Sql
через hibernate.metadata_builder_contributor
свойство конфигурации:
name="hibernate.metadata_builder_contributor" value="com.vladmihalcea.book.hpjp.hibernate.query.function.SqlFunctionsMetadataBuilderContributor"
Вывод
Хотя вы всегда можете запустить собственный SQL-запрос, если хотите воспользоваться функциями, специфичными для базы данных, которые недоступны в JPQL, если вы создаете JPQL динамически с помощью API критериев, вы можете вызвать любую функцию SQL, если Hibernate знает об этом.