From d1d782cd1bd6ac2ac6102b7ec7bea44c686f2641 Mon Sep 17 00:00:00 2001 From: Yannick Majoros Date: Tue, 7 Nov 2023 17:35:30 +0100 Subject: [PATCH 1/3] resolves #668 --- .../utils/hibernate/query/SQLExtractor.java | 46 ++++++++--- .../hibernate/query/SQLExtractorTest.java | 76 +++++++++++++++---- 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java index 5183c5447..5bca7aa7a 100644 --- a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java +++ b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java @@ -13,6 +13,9 @@ import org.hibernate.query.sqm.tree.select.SqmSelectStatement; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; import java.util.function.Supplier; /** @@ -36,20 +39,21 @@ protected SQLExtractor() { * @return the underlying SQL generated by the provided JPA query */ public static String from(Query query) { - if(query instanceof SqmInterpretationsKey.InterpretationsKeySource && - query instanceof QueryImplementor && - query instanceof QuerySqmImpl) { - QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey((SqmInterpretationsKey.InterpretationsKeySource) query); - QuerySqmImpl querySqm = (QuerySqmImpl) query; + Query hibernateQuery = getHibernateQuery(query); + if (hibernateQuery instanceof SqmInterpretationsKey.InterpretationsKeySource && + hibernateQuery instanceof QueryImplementor && + hibernateQuery instanceof QuerySqmImpl) { + QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey((SqmInterpretationsKey.InterpretationsKeySource) hibernateQuery); + QuerySqmImpl querySqm = (QuerySqmImpl) hibernateQuery; Supplier buildSelectQueryPlan = () -> ReflectionUtils.invokeMethod(querySqm, "buildSelectQueryPlan"); - SelectQueryPlan plan = cacheKey != null ? ((QueryImplementor) query).getSession().getFactory().getQueryEngine() + SelectQueryPlan plan = cacheKey != null ? ((QueryImplementor) hibernateQuery).getSession().getFactory().getQueryEngine() .getInterpretationCache() .resolveSelectQueryPlan(cacheKey, buildSelectQueryPlan) : (SelectQueryPlan) buildSelectQueryPlan.get(); - if(plan instanceof ConcreteSqmSelectQueryPlan) { + if (plan instanceof ConcreteSqmSelectQueryPlan) { ConcreteSqmSelectQueryPlan selectQueryPlan = (ConcreteSqmSelectQueryPlan) plan; Object cacheableSqmInterpretation = ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "cacheableSqmInterpretation"); - if(cacheableSqmInterpretation == null) { + if (cacheableSqmInterpretation == null) { DomainQueryExecutionContext domainQueryExecutionContext = DomainQueryExecutionContext.class.cast(querySqm); cacheableSqmInterpretation = ReflectionUtils.invokeStaticMethod( ReflectionUtils.getMethod( @@ -72,6 +76,30 @@ public static String from(Query query) { } } } - return ReflectionUtils.invokeMethod(query, "getQueryString"); + return ReflectionUtils.invokeMethod(hibernateQuery, "getQueryString"); + } + + /** + * Get the unproxied hibernate query underlying the provided query object. + * + * @param query JPA query + * @return the unproxied Hibernate query, or original query + */ + private static Query getHibernateQuery(Query query) { + try { + if (query instanceof QuerySqmImpl || !Proxy.isProxyClass(query.getClass())) { + return query; + } + // is proxyied, get it out + InvocationHandler invocationHandler = Proxy.getInvocationHandler(query); + Class innerClass = invocationHandler.getClass(); + Field targetField = innerClass.getDeclaredField("target"); + targetField.setAccessible(true); + return (Query) targetField.get(invocationHandler); + } catch (NoSuchFieldException exception) { + return query; // seems it cannot extract it, probably not a hibernate proxy + } catch (IllegalAccessException exception) { + throw new RuntimeException(exception); + } } } diff --git a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java index e74d81673..8fbbe4fd9 100644 --- a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java +++ b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java @@ -1,13 +1,24 @@ package io.hypersistence.utils.hibernate.query; import io.hypersistence.utils.hibernate.util.AbstractPostgreSQLIntegrationTest; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityManager; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Query; +import jakarta.persistence.Table; +import jakarta.persistence.Tuple; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Root; import org.junit.Test; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.time.LocalDate; import static org.junit.Assert.assertNotNull; @@ -53,24 +64,27 @@ public void testJPQL() { @Test public void testCriteriaAPI() { doInJPA(entityManager -> { - CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + Query criteriaQuery = createTestQuery(entityManager); - CriteriaQuery criteria = builder.createQuery(PostComment.class); - - Root postComment = criteria.from(PostComment.class); - Join post = postComment.join("post"); + String sql = SQLExtractor.from(criteriaQuery); - criteria.where( - builder.like(post.get("title"), "%Java%") - ); + assertNotNull(sql); - criteria.orderBy( - builder.asc(postComment.get("id")) + LOGGER.info( + "The Criteria API query: [\n{}\n]\ngenerates the following SQL query: [\n{}\n]", + criteriaQuery.unwrap(org.hibernate.query.Query.class).getQueryString(), + sql ); + }); + } - Query criteriaQuery = entityManager.createQuery(criteria); + @Test + public void testCriteriaAPIWithProxy() { + doInJPA(entityManager -> { + Query criteriaQuery = createTestQuery(entityManager); + Query proxiedQuery = proxy(criteriaQuery); - String sql = SQLExtractor.from(criteriaQuery); + String sql = SQLExtractor.from(proxiedQuery); assertNotNull(sql); @@ -82,6 +96,29 @@ public void testCriteriaAPI() { }); } + private static Query proxy(Query criteriaQuery) { + return (Query) Proxy.newProxyInstance(Query.class.getClassLoader(), new Class[]{Query.class}, new HibernateLikeInvocationHandler(criteriaQuery)); + } + + private static Query createTestQuery(EntityManager entityManager) { + CriteriaBuilder builder = entityManager.getCriteriaBuilder(); + + CriteriaQuery criteria = builder.createQuery(PostComment.class); + + Root postComment = criteria.from(PostComment.class); + Join post = postComment.join("post"); + + criteria.where( + builder.like(post.get("title"), "%Java%") + ); + + criteria.orderBy( + builder.asc(postComment.get("id")) + ); + + return entityManager.createQuery(criteria); + } + @Entity(name = "Post") @Table(name = "post") public static class Post { @@ -161,4 +198,17 @@ public PostComment setReview(String review) { return this; } } + + private static class HibernateLikeInvocationHandler implements InvocationHandler { + private final Query target; // has to be named "target" because this is how Hibernate implements it, and the extracting code has to be quite invasive to get the query from the Hibernate proxy + + public HibernateLikeInvocationHandler(Query query) { + this.target = query; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(target, args); + } + } } From c4e39ef5f98b1746fceb2f156eda3654248ac80b Mon Sep 17 00:00:00 2001 From: Yannick Majoros Date: Wed, 8 Nov 2023 12:40:56 +0100 Subject: [PATCH 2/3] resolves #674 - extract parameters --- .../utils/hibernate/query/SQLExtractor.java | 133 ++++++++++++------ .../hibernate/query/SQLExtractorTest.java | 68 +++++++-- .../type/basic/PostgreSQLEnumAuditTest.java | 11 ++ 3 files changed, 157 insertions(+), 55 deletions(-) diff --git a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java index 5bca7aa7a..62ce9c95b 100644 --- a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java +++ b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java @@ -1,10 +1,12 @@ package io.hypersistence.utils.hibernate.query; import io.hypersistence.utils.hibernate.util.ReflectionUtils; +import jakarta.persistence.Parameter; import jakarta.persistence.Query; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.query.spi.QueryInterpretationCache; +import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.SelectQueryPlan; import org.hibernate.query.sqm.internal.ConcreteSqmSelectQueryPlan; import org.hibernate.query.sqm.internal.DomainParameterXref; @@ -16,7 +18,11 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * The {@link SQLExtractor} allows you to extract the @@ -39,67 +45,110 @@ protected SQLExtractor() { * @return the underlying SQL generated by the provided JPA query */ public static String from(Query query) { - Query hibernateQuery = getHibernateQuery(query); - if (hibernateQuery instanceof SqmInterpretationsKey.InterpretationsKeySource && - hibernateQuery instanceof QueryImplementor && - hibernateQuery instanceof QuerySqmImpl) { - QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey((SqmInterpretationsKey.InterpretationsKeySource) hibernateQuery); - QuerySqmImpl querySqm = (QuerySqmImpl) hibernateQuery; - Supplier buildSelectQueryPlan = () -> ReflectionUtils.invokeMethod(querySqm, "buildSelectQueryPlan"); - SelectQueryPlan plan = cacheKey != null ? ((QueryImplementor) hibernateQuery).getSession().getFactory().getQueryEngine() - .getInterpretationCache() - .resolveSelectQueryPlan(cacheKey, buildSelectQueryPlan) : - (SelectQueryPlan) buildSelectQueryPlan.get(); - if (plan instanceof ConcreteSqmSelectQueryPlan) { - ConcreteSqmSelectQueryPlan selectQueryPlan = (ConcreteSqmSelectQueryPlan) plan; - Object cacheableSqmInterpretation = ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "cacheableSqmInterpretation"); - if (cacheableSqmInterpretation == null) { - DomainQueryExecutionContext domainQueryExecutionContext = DomainQueryExecutionContext.class.cast(querySqm); - cacheableSqmInterpretation = ReflectionUtils.invokeStaticMethod( - ReflectionUtils.getMethod( - ConcreteSqmSelectQueryPlan.class, - "buildCacheableSqmInterpretation", - SqmSelectStatement.class, - DomainParameterXref.class, - DomainQueryExecutionContext.class - ), - ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "sqm"), - ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "domainParameterXref"), - domainQueryExecutionContext - ); - } - if (cacheableSqmInterpretation != null) { - JdbcOperationQuerySelect jdbcSelect = ReflectionUtils.getFieldValueOrNull(cacheableSqmInterpretation, "jdbcSelect"); - if (jdbcSelect != null) { - return jdbcSelect.getSqlString(); - } + return getSqmQueryOptional(query) + .map(SQLExtractor::getSQLFromSqmQuery) + .orElseGet(() -> ReflectionUtils.invokeMethod(query, "getQueryString")); + } + + private static String getSQLFromSqmQuery(QuerySqmImpl querySqm) { + QueryInterpretationCache.Key cacheKey = SqmInterpretationsKey.createInterpretationsKey(querySqm); + Supplier> buildSelectQueryPlan = () -> ReflectionUtils.invokeMethod(querySqm, "buildSelectQueryPlan"); + SelectQueryPlan plan = cacheKey != null ? ((QueryImplementor) querySqm).getSession().getFactory().getQueryEngine() + .getInterpretationCache() + .resolveSelectQueryPlan(cacheKey, buildSelectQueryPlan) : + buildSelectQueryPlan.get(); + if (plan instanceof ConcreteSqmSelectQueryPlan) { + ConcreteSqmSelectQueryPlan selectQueryPlan = (ConcreteSqmSelectQueryPlan) plan; + Object cacheableSqmInterpretation = ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "cacheableSqmInterpretation"); + if (cacheableSqmInterpretation == null) { + cacheableSqmInterpretation = ReflectionUtils.invokeStaticMethod( + ReflectionUtils.getMethod( + ConcreteSqmSelectQueryPlan.class, + "buildCacheableSqmInterpretation", + SqmSelectStatement.class, + DomainParameterXref.class, + DomainQueryExecutionContext.class + ), + ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "sqm"), + ReflectionUtils.getFieldValueOrNull(selectQueryPlan, "domainParameterXref"), + querySqm + ); + } + if (cacheableSqmInterpretation != null) { + JdbcOperationQuerySelect jdbcSelect = ReflectionUtils.getFieldValueOrNull(cacheableSqmInterpretation, "jdbcSelect"); + if (jdbcSelect != null) { + return jdbcSelect.getSqlString(); } } } - return ReflectionUtils.invokeMethod(hibernateQuery, "getQueryString"); + return querySqm.getQueryString(); + } + + public static List getSQLParameterValues(Query query) { + return getSqmQueryOptional(query) + .map(SQLExtractor::getParametersFromInternalQuerySqm) + .orElseGet(() -> getSQLParametersFromJPAQuery(query)); + } + + /** + * Retrieves the parameters from the internal query SQM. + * + * @param querySqm the internal query SQM object + * @return a list of parameter values + */ + private static List getParametersFromInternalQuerySqm(QuerySqmImpl querySqm) { + List parameterValues = new ArrayList<>(); + + QueryParameterBindings parameterBindings = querySqm.getParameterBindings(); + parameterBindings.visitBindings((queryParameterImplementor, queryParameterBinding) -> { + Object value = queryParameterBinding.getBindValue(); + parameterValues.add(value); + }); + + return parameterValues; + } + + /** + * Get parameters from JPA query without any magic or Hibernate implementation tricks. Order is probably lost in current Hibernate versions. + * + * @param query + * @return + */ + private static List getSQLParametersFromJPAQuery(Query query) { + return query.getParameters() + .stream() + .map(Parameter::getPosition) + .map(query::getParameter) + .collect(Collectors.toList()); } + /** * Get the unproxied hibernate query underlying the provided query object. * * @param query JPA query - * @return the unproxied Hibernate query, or original query + * @return the unproxied Hibernate query, or original query if there is no proxy, or null if it's not an Hibernate query of required type */ - private static Query getHibernateQuery(Query query) { + private static Optional> getSqmQueryOptional(Query query) { try { - if (query instanceof QuerySqmImpl || !Proxy.isProxyClass(query.getClass())) { - return query; + if (query instanceof QuerySqmImpl) { + QuerySqmImpl querySqm = (QuerySqmImpl) query; + return Optional.of(querySqm); + } + if (!Proxy.isProxyClass(query.getClass())) { + return Optional.empty(); } // is proxyied, get it out InvocationHandler invocationHandler = Proxy.getInvocationHandler(query); Class innerClass = invocationHandler.getClass(); Field targetField = innerClass.getDeclaredField("target"); targetField.setAccessible(true); - return (Query) targetField.get(invocationHandler); + QuerySqmImpl querySqm = (QuerySqmImpl) targetField.get(invocationHandler); + return Optional.of(querySqm); } catch (NoSuchFieldException exception) { - return query; // seems it cannot extract it, probably not a hibernate proxy + return Optional.empty(); // not an Hibernate query } catch (IllegalAccessException exception) { - throw new RuntimeException(exception); + throw new IllegalStateException(exception); } } } diff --git a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java index 8fbbe4fd9..e07dac060 100644 --- a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java +++ b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/query/SQLExtractorTest.java @@ -10,6 +10,7 @@ import jakarta.persistence.Query; import jakarta.persistence.Table; import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Join; @@ -20,7 +21,9 @@ import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.time.LocalDate; +import java.util.List; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; /** @@ -39,15 +42,7 @@ protected Class[] entities() { @Test public void testJPQL() { doInJPA(entityManager -> { - Query jpql = entityManager - .createQuery( - "select " + - " YEAR(p.createdOn) as year, " + - " count(p) as postCount " + - "from " + - " Post p " + - "group by " + - " YEAR(p.createdOn)", Tuple.class); + Query jpql = createTestJPQL(entityManager); String sql = SQLExtractor.from(jpql); @@ -60,11 +55,10 @@ public void testJPQL() { ); }); } - @Test public void testCriteriaAPI() { doInJPA(entityManager -> { - Query criteriaQuery = createTestQuery(entityManager); + Query criteriaQuery = createTestCriteriaQuery(entityManager); String sql = SQLExtractor.from(criteriaQuery); @@ -81,7 +75,7 @@ public void testCriteriaAPI() { @Test public void testCriteriaAPIWithProxy() { doInJPA(entityManager -> { - Query criteriaQuery = createTestQuery(entityManager); + Query criteriaQuery = createTestCriteriaQuery(entityManager); Query proxiedQuery = proxy(criteriaQuery); String sql = SQLExtractor.from(proxiedQuery); @@ -96,11 +90,59 @@ public void testCriteriaAPIWithProxy() { }); } + @Test + public void testJPQLGetSQLParameters() { + doInJPA(entityManager -> { + Query jpql = createTestJPQL(entityManager); + + List parameters = SQLExtractor.getSQLParameterValues(jpql); + + assertFalse(parameters.isEmpty()); + + LOGGER.info( + "The Criteria API query: [\n{}\n]\nhas following SQL parameters: \n{}\n", + jpql.unwrap(org.hibernate.query.Query.class).getQueryString(), + parameters + ); + }); + } + + @Test + public void testCriteriaGetSQLParameters() { + doInJPA(entityManager -> { + Query criteriaQuery = createTestCriteriaQuery(entityManager); + + List parameters = SQLExtractor.getSQLParameterValues(criteriaQuery); + + assertFalse(parameters.isEmpty()); + + LOGGER.info( + "The Criteria API query: [\n{}\n]\nhas following SQL parameters: \n{}\n", + criteriaQuery.unwrap(org.hibernate.query.Query.class).getQueryString(), + parameters + ); + }); + } + private static Query proxy(Query criteriaQuery) { return (Query) Proxy.newProxyInstance(Query.class.getClassLoader(), new Class[]{Query.class}, new HibernateLikeInvocationHandler(criteriaQuery)); } - private static Query createTestQuery(EntityManager entityManager) { + private static Query createTestJPQL(EntityManager entityManager) { + Query jpql = entityManager + .createQuery( + "select " + + " YEAR(p.createdOn) as year, " + + " count(p) as postCount " + + "from Post p " + + "where p.title like :titleTemplate " + + "group by YEAR(p.createdOn) ", + Tuple.class); + jpql.setParameter("titleTemplate", "%Java%"); + return jpql; + } + + private static Query createTestCriteriaQuery(EntityManager entityManager) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery criteria = builder.createQuery(PostComment.class); diff --git a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/basic/PostgreSQLEnumAuditTest.java b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/basic/PostgreSQLEnumAuditTest.java index 080f37fd3..c1a4283e6 100644 --- a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/basic/PostgreSQLEnumAuditTest.java +++ b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/basic/PostgreSQLEnumAuditTest.java @@ -12,6 +12,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; +import java.util.Date; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @@ -103,6 +104,8 @@ public static class Post { @Type(PostgreSQLEnumType.class) private PostStatus status; + private Date createdOn; + public Long getId() { return id; } @@ -126,5 +129,13 @@ public PostStatus getStatus() { public void setStatus(PostStatus status) { this.status = status; } + + public Date getCreatedOn() { + return createdOn; + } + + public void setCreatedOn(Date createdOn) { + this.createdOn = createdOn; + } } } From 8b260bed373cc56a2cdbee3d59d0bee730392731 Mon Sep 17 00:00:00 2001 From: Yannick Majoros Date: Mon, 13 Nov 2023 08:58:10 +0100 Subject: [PATCH 3/3] incorporating feedback from https://github.com/vladmihalcea/hypersistence-utils/pull/673#issuecomment-1806749757 --- .../utils/hibernate/query/SQLExtractor.java | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java index 62ce9c95b..a4492c57a 100644 --- a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java +++ b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/query/SQLExtractor.java @@ -130,25 +130,11 @@ private static List getSQLParametersFromJPAQuery(Query query) { * @return the unproxied Hibernate query, or original query if there is no proxy, or null if it's not an Hibernate query of required type */ private static Optional> getSqmQueryOptional(Query query) { - try { - if (query instanceof QuerySqmImpl) { - QuerySqmImpl querySqm = (QuerySqmImpl) query; - return Optional.of(querySqm); - } - if (!Proxy.isProxyClass(query.getClass())) { - return Optional.empty(); - } - // is proxyied, get it out - InvocationHandler invocationHandler = Proxy.getInvocationHandler(query); - Class innerClass = invocationHandler.getClass(); - Field targetField = innerClass.getDeclaredField("target"); - targetField.setAccessible(true); - QuerySqmImpl querySqm = (QuerySqmImpl) targetField.get(invocationHandler); + Query unwrappedQuery = query.unwrap(Query.class); + if (unwrappedQuery instanceof QuerySqmImpl) { + QuerySqmImpl querySqm = (QuerySqmImpl) unwrappedQuery; return Optional.of(querySqm); - } catch (NoSuchFieldException exception) { - return Optional.empty(); // not an Hibernate query - } catch (IllegalAccessException exception) { - throw new IllegalStateException(exception); } + return Optional.empty(); } }