我可以从 JPA 查询对象获取 SQL 字符串吗?

发布于 2024-11-14 13:17:11 字数 61 浏览 2 评论 0原文

我可以知道如何从 JPA 查询中获取 sql 吗?或者说,将 JPA 查询转换为 SQL 字符串?非常感谢!

May I know how can I get the sql from a JPA query? or let's say, convert the JPA query to a SQL string? Thank you very much!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

弥繁 2024-11-21 13:17:11

对于 Eclipselink:您可以通过以下方式提取 SQL:

query.unwrap(EJBQueryImpl.class).getDatabaseQuery().getSQLString()

它仅在执行查询之后后才起作用。

For Eclipselink: you can extract the SQL the following way:

query.unwrap(EJBQueryImpl.class).getDatabaseQuery().getSQLString()

It works only after the query has been executed.

筱武穆 2024-11-21 13:17:11

JPA 规范

虽然没有标准 JPA 功能可以实现此目标,但您仍然可以使用 JPA 提供程序特定的 API 从 JPQL 或 Criteria API 查询 中提取 SQL 查询。

Hypersistence Utils

Hypersistence Utils 开源项目提供了 SQLExtractor 实用程序它允许您从任何 JPQL 或 Criteria API 查询中获取 SQL 查询,无论您使用的是 Hibernate 6.2、6.1、5.6、 5.5、5.4、5.3、5.2、5.1 或 5.0。

从 JPQL 查询获取 SQL 语句

假设我们有以下 JPQL 查询:

Query jpql = entityManager.createQuery("""
    select 
       YEAR(p.createdOn) as year, 
       count(p) as postCount 
    from 
       Post p 
    group by 
       YEAR(p.createdOn)
    """, Tuple.class
);

使用 Hibernate 类型,提取 Hibernate 生成的 SQL 查询就这么简单:

String sql = SQLExtractor.from(jpql);

而且,如果我们记录提取的 SQL 查询:

LOGGER.info("""
    The JPQL query: [
        {}
    ]
    generates the following SQL query: [ 
        {}
    ]
    """,
    jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
    sql
);

我们会得到以下输出:

- The JPQL query: [
    select    
        YEAR(p.createdOn) as year,    
        count(p) as postCount 
    from    
        Post p 
    group by    
        YEAR(p.createdOn)
]
generates the following SQL query: [
    SELECT 
        extract(YEAR FROM sqlextract0_.created_on) AS col_0_0_,
        count(sqlextract0_.id) AS col_1_0_
    FROM 
        post p
    GROUP BY 
        extract(YEAR FROM p.created_on)
]

请注意,我们将 JPA Query 解包到 Hibernate org.hibernate.query.Query 接口,该接口提供了我们可以使用的 getQueryString 方法记录关联的 JPQL 查询字符串。

从 JPA Criteria API 查询获取 SQL 语句

SQLExtractor 不限于 JPQL 查询。您也可以将其与 Criteria API 查询一起使用,如以下示例所示:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<PostComment> criteria = builder.createQuery(PostComment.class);

Root<PostComment> postComment = criteria.from(PostComment.class);
Join<PostComment, Post> post = postComment.join("post");

criteria.where(
    builder.like(post.get("title"), "%Java%")
);

criteria.orderBy(
    builder.asc(postComment.get("id"))
);

Query criteriaQuery = entityManager.createQuery(criteria);

String sql = SQLExtractor.from(criteriaQuery);

assertNotNull(sql);

LOGGER.info("""
    The Criteria API, compiled to this JPQL query: [
        {}
    ]
    generates the following SQL query: [
        {}
    ]
    """,
    jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
    sql
);

运行上述测试用例时,我们得到以下 SQL 查询:

- The Criteria API, compiled to this JPQL query: [
    select 
        pc 
    from 
        PostComment as pc 
    inner join 
        pc.post as p 
    where 
        p.title like :param0 
    order by 
        pc.id asc
]
generates the following SQL query: [
    SELECT 
        pc.id AS id1_1_,
        pc.post_id AS post_id3_1_,
        pc.review AS review2_1_
    FROM 
        post_comment pc
    INNER JOIN 
        post p ON pc.post_id=p.id
    WHERE 
        p.title LIKE ?
    ORDER BY 
        pc.id ASC
]

Criteria API 首先编译为 JPQL 查询,如 所示getQueryString() 方法调用。

中间 JPQL 查询进一步转换为 SQL 查询,并由 SQLExtractor 实用程序正确解析。

JPA Specification

While there is not standard JPA functionality to achieve this goal, you can still extract the SQL query from a JPQL or Criteria API Query using the JPA provider-specific API.

Hypersistence Utils

The Hypersistence Utils open-source project offers the SQLExtractor utility that allows you to get the SQL query from any JPQL or Criteria API query, no matter you are using Hibernate 6.2, 6.1, 5.6, 5.5, 5.4, 5.3, 5.2, 5.1, or 5.0.

Get the SQL statement from a JPQL Query

Let's assume we have the following JPQL query:

Query jpql = entityManager.createQuery("""
    select 
       YEAR(p.createdOn) as year, 
       count(p) as postCount 
    from 
       Post p 
    group by 
       YEAR(p.createdOn)
    """, Tuple.class
);

With Hibernate Types, extracting the Hibernate-generated SQL query is as simple as that:

String sql = SQLExtractor.from(jpql);

And, if we log the extracted SQL query:

LOGGER.info("""
    The JPQL query: [
        {}
    ]
    generates the following SQL query: [ 
        {}
    ]
    """,
    jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
    sql
);

We get the following output:

- The JPQL query: [
    select    
        YEAR(p.createdOn) as year,    
        count(p) as postCount 
    from    
        Post p 
    group by    
        YEAR(p.createdOn)
]
generates the following SQL query: [
    SELECT 
        extract(YEAR FROM sqlextract0_.created_on) AS col_0_0_,
        count(sqlextract0_.id) AS col_1_0_
    FROM 
        post p
    GROUP BY 
        extract(YEAR FROM p.created_on)
]

Notice that we unwrapped the JPA Query to the Hibernate org.hibernate.query.Query interface which provided the getQueryString method we can use to log the associated JPQL query string.

Get the SQL statement from a JPA Criteria API Query

The SQLExtractor is not limited to JPQL queries. You can use it with Criteria API queries as well, as illustrated by the following example:

CriteriaBuilder builder = entityManager.getCriteriaBuilder();

CriteriaQuery<PostComment> criteria = builder.createQuery(PostComment.class);

Root<PostComment> postComment = criteria.from(PostComment.class);
Join<PostComment, Post> post = postComment.join("post");

criteria.where(
    builder.like(post.get("title"), "%Java%")
);

criteria.orderBy(
    builder.asc(postComment.get("id"))
);

Query criteriaQuery = entityManager.createQuery(criteria);

String sql = SQLExtractor.from(criteriaQuery);

assertNotNull(sql);

LOGGER.info("""
    The Criteria API, compiled to this JPQL query: [
        {}
    ]
    generates the following SQL query: [
        {}
    ]
    """,
    jpql.unwrap(org.hibernate.query.Query.class).getQueryString(),
    sql
);

When running the above test case, we get the following SQL query:

- The Criteria API, compiled to this JPQL query: [
    select 
        pc 
    from 
        PostComment as pc 
    inner join 
        pc.post as p 
    where 
        p.title like :param0 
    order by 
        pc.id asc
]
generates the following SQL query: [
    SELECT 
        pc.id AS id1_1_,
        pc.post_id AS post_id3_1_,
        pc.review AS review2_1_
    FROM 
        post_comment pc
    INNER JOIN 
        post p ON pc.post_id=p.id
    WHERE 
        p.title LIKE ?
    ORDER BY 
        pc.id ASC
]

The Criteria API is first compiled to a JPQL query, as illustrated by the getQueryString() method call.

The intermediary JPQL query is further translated to an SQL query, which is properly resolved by the SQLExtractor utility.

剩余の解释 2024-11-21 13:17:11

如果您只想知道 JPQL 或条件查询如何转换为数据库的 SQL 方言,您可以在 persistence xml 中启用细粒度日志记录,然后查看日志文件。

属性名称和值取决于您的 JPA 实现。以下是 EclipseLink 的 persistence.xml 相关部分的示例:

<properties>
    <property name="eclipselink.logging.level" value="FINEST"/>
</properties>

If you only want to know how your JPQL or Criteria Query gets translated to the SQL dialect of your database you can enable fine grained logging in the persistence xml and then look into your log files.

The property name and value depends on your JPA implementation. Here is an example of the relevant part of persistence.xml for EclipseLink:

<properties>
    <property name="eclipselink.logging.level" value="FINEST"/>
</properties>
请持续率性 2024-11-21 13:17:11

按照 Karol 的回答 - 在 EclipseLink 中执行语句之前可以检索 SQL :

Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
DatabaseQuery databaseQuery = query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
databaseQuery.prepareCall(session, new DatabaseRecord());
Record r = databaseQuery.getTranslationRow();
String bound = databaseQuery.getTranslatedSQLString(session, r);
String sqlString = databaseQuery.getSQLString();

要在执行期间/之后检索 SQL 字符串,最好使用持久性属性而不是代码内来执行此操作:

<property name="eclipselink.logging.parameters" value="true"/>
<property name="eclipselink.logging.level" value="FINE"/>

Following Karol's answer - It is possible to retrieve the SQL before executing the statement in EclipseLink :

Session session = em.unwrap(JpaEntityManager.class).getActiveSession();
DatabaseQuery databaseQuery = query.unwrap(EJBQueryImpl.class).getDatabaseQuery();
databaseQuery.prepareCall(session, new DatabaseRecord());
Record r = databaseQuery.getTranslationRow();
String bound = databaseQuery.getTranslatedSQLString(session, r);
String sqlString = databaseQuery.getSQLString();

To retrieve the SQL String During/After execution it is probably best to do so using persistence properties rather than in-code :

<property name="eclipselink.logging.parameters" value="true"/>
<property name="eclipselink.logging.level" value="FINE"/>
木森分化 2024-11-21 13:17:11

除了像 @Matt Handy 提到的那样启用日志记录之外,还可以在运行时使用 eclipselink 获取特定查询的 SQL 字符串,如所述 此处

Beside enabling the logging like @Matt Handy mentioned it is also possible to get the SQL String for a specific query with eclipselink at runtime as described here.

病女 2024-11-21 13:17:11

使用 Hibernate 作为提供程序,您可以启用以下属性:

hibernate.show_sql  

将所有 SQL 语句写入控制台。这是设置日志类别 org.hibernate.SQL 进行调试的替代方法。 (例如 true | false)

hibernate.format_sql

在日志和控制台中漂亮地打印 SQL。 (例如 true | false)

或者,如上所述,您可以启用记录器的调试级别日志

org.hibernate.SQL

记录所有执行的 SQL DML 语句

Using Hibernate as a provider you can enable the following properties:

hibernate.show_sql  

Write all SQL statements to console. This is an alternative to setting the log category org.hibernate.SQL to debug. (e.g. true | false)

hibernate.format_sql

Pretty print the SQL in the log and console. (e.g. true | false)

Or, as stated above you can enable logging to the debug level for the logger

org.hibernate.SQL

Log all SQL DML statements as they are executed

辞慾 2024-11-21 13:17:11

您可能感兴趣是否有一种方法可以从 javax.persistence.Query (更准确地说,它可能是子类之一)中“提取”JPQL 字符串(带有参数的占位符,或者填充参数后的最终 JPQL), - 在这种情况下,根据 JPA 规范合同,这是不可能的。然而,假设这可能通过 JPA 实现实现(例如,NamedQueryImpl 可以有 #toJPQLString(),您可以通过转换访问它),但我对此表示怀疑。
即使有可能,我也不认为这是执行此类操作的好代码。我建议寻找另一种设计解决方案(为此您可以指定您遇到的实际问题类型)。例如,如果您动态构建查询,则可以使用 JPA Criteria API,并且与“构建”JPA 查询一起,您可以维护反映查询逻辑的内部数据结构。

You are probably interested if there's a way to 'extract' JPQL string (either with placeholders for params, or final JPQL after params are filled-in) out of javax.persistence.Query (one of it's possible subclasses to be more precise),- in this case it's not possible according to JPA specification contract. However, this hypothetically might be possible by JPA implementation (e.g., NamedQueryImpl could have #toJPQLString(), which you could access via casting), but I doubt about that.
And even if it's possible I don't think it's a good code performing such manipulations. I would suggest finding another design solutions (and for that you could specify what kind of actual problem do you have). E.g., if you are building your queries dynamically, JPA Criteria API could be used for that, and along with 'building' JPA query, you could maintain your internal data structure reflecting the logic of your query.

青巷忧颜 2024-11-21 13:17:11

你可以使用 p6spy。以下链接提供了操作说明:

You could use p6spy. At the following link there are instructions for its operation:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文