我可以从JPA Query对象中获取SQL字符串吗?

1dkrff03  于 2023-10-19  发布在  其他
关注(0)|答案(9)|浏览(143)

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

czq61nw1

czq61nw11#

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

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

它只在**查询执行后才起作用。

33qvvth1

33qvvth12#

JPA规范

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

Hyperpersistence 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 Query获取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实用程序正确解析。

bqjvbblv

bqjvbblv3#

如果你只想知道你的JPQL或Criteria Query是如何被翻译成数据库的SQL方言的,你可以在 persistence xml 中启用细粒度的日志记录,然后查看日志文件。
属性名称和值取决于您的JPA实现。下面是 persistence.xml 的相关部分的一个例子:

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

zqry0prt4#

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

m3eecexj5#

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

tjrkku2a

tjrkku2a6#

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

hibernate.show_sql

将所有SQL语句写入控制台。这是将日志类别org.hibernate.SQL设置为debug的替代方法。(例如:true|假的)

hibernate.format_sql

在日志和控制台中打印SQL。(例如:true|假的)
或者,如上所述,您可以启用日志记录器的调试级别

org.hibernate.SQL

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

htzpubme

htzpubme7#

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

s2j5cfk0

s2j5cfk09#

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

相关问题