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

问题描述 投票:0回答:9

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

sql hibernate jpa logging jpql
9个回答
21
投票

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

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

它仅在执行查询之后才有效。


11
投票

JPA规范

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

Query
提取 SQL 查询。

休眠类型

从 2.9.11 版本开始,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
实用程序正确解析。


10
投票

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

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

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

4
投票

按照 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"/>

3
投票

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


3
投票

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

hibernate.show_sql  

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

hibernate.format_sql

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

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

org.hibernate.SQL

在执行时记录所有 SQL DML 语句


2
投票

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



0
投票

你可以使用p6spy。以下链接有操作说明:

© www.soinside.com 2019 - 2024. All rights reserved.