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

时间:2011-06-08 08:29:28

标签: sql jpa

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

7 个答案:

答案 0 :(得分:11)

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

var contact = m_someFactory.Create<IContact>(contact, Page.ID);
var organization = contact.Organization;

仅在执行 后才能使用

答案 1 :(得分:7)

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

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

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

答案 2 :(得分:4)

由于这是一个非常常见的问题,所以此答案基于我在博客上写的this article

JPA规范

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

休眠类型

从2.9.11版本开始,Hibernate Types开源项目提供了SQLExtractor实用程序,无论您使用什么,该实用程序都可以从任何JPQL或Criteria API查询中获取SQL查询。 Hibernate 5.4、5.3、5.2、5.1、5.0、4.3、4.2或4.1。

从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标准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实用程序正确地解决。

有关此功能的更多详细信息,请查看this article

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

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

hibernate.show_sql  

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

hibernate.format_sql

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

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

org.hibernate.SQL

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

答案 6 :(得分:0)