如何以编程方式将 HQL 转换为 SQL 查询(不记录)

Posted

技术标签:

【中文标题】如何以编程方式将 HQL 转换为 SQL 查询(不记录)【英文标题】:How to convert HQL to SQL Query programmatically (without logging) 【发布时间】:2013-08-25 09:22:12 【问题描述】:

我正在执行以下 HQL,它正在正确执行

String hql = "FROM Employee";
Query query = session.createQuery(hql);
List results = query.list();

现在,我还想将后端生成的 sql 记录在日志中以供支持用户使用。

我想使用QueryTranslator,请指教如何为相应的HQL生成sql,请指教如何实现。

【问题讨论】:

【参考方案1】:

你可以使用hibernate QueryTranslator:

String hqlQueryString = hqlQuery.getQueryString();
ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
SessionImplementor hibernateSession = entityManager.unwrap(SessionImplementor.class);
QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator("", hqlQueryString, java.util.Collections.EMPTY_MAP, hibernateSession.getFactory());
queryTranslator.compile(java.util.Collections.EMPTY_MAP, false);
String sqlQueryString = queryTranslator.getSQLString();

【讨论】:

不幸的是,这不适用于 Hibernate 5 :( 有新的第 5 个参数 EntityGraphQueryHint 您可以将 null 作为第 5 个参数传递。它应该可以工作。 这只会返回 hql 而不是 sql;问题是:“现在我还想将后端生成的 sql 记录在支持用户的日志中” 代码最终返回“sqlQueryString”变量中hqlQuery的SQL等价。 很遗憾没有使用列的别名。【参考方案2】:

我相信您想要前 2 个答案的组合

  String hqlQueryString = query.unwrap(org.hibernate.Query.class).getQueryString();
  ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
  SessionImplementor hibernateSession = em.unwrap(SessionImplementor.class);
  QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator("", hqlQueryString, java.util.Collections.EMPTY_MAP, hibernateSession.getFactory());
  queryTranslator.compile(java.util.Collections.EMPTY_MAP, false);
  String sqlQueryString = queryTranslator.getSQLString();

【讨论】:

createQueryTranslator 还需要一个参数:EntityGraphQueryHint 翻译后,“:variable”等参数全部被“?”替换,无法设置参数值。我也尝试设置参数,然后将其转换为 SQL,但效果不佳。注意:我这样做是为了将其附加为计数的子查询,因为 group by 内有许多参数,而 HQL 不支持这个【参考方案3】:

在 Hibernate 的更高版本中使用 TypedQuery 也可以使用以下代码

String hqlQueryString=typedQuery.unwrap(org.hibernate.query.Query.class).getQueryString();
        ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
        SessionImplementor hibernateSession = entityManager.unwrap(SessionImplementor.class);
        QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator("", hqlQueryString, java.util.Collections.EMPTY_MAP, hibernateSession.getFactory(), null);
        queryTranslator.compile(java.util.Collections.EMPTY_MAP, false);
        String sqlQueryString = queryTranslator.getSQLString();

【讨论】:

【参考方案4】:

我在网上找到了下一个解决方案:

QueryTranslatorFactory translatorFactory = new ASTQueryTranslatorFactory();
SessionFactoryImplementor factory = (SessionFactoryImplementor) getSessionFactory();
QueryTranslator translator = translatorFactory.
        createQueryTranslator(hqlQueryText, hqlQueryText, Collections.EMPTY_MAP, factory);
translator.compile(Collections.EMPTY_MAP, false);
translator.getSQLString(); 

来源:http://narcanti.keyboardsamurais.de/hibernate-hql-to-sql-translation.html

【讨论】:

【参考方案5】:

这行得通,其他答案对于现代版本的休眠有问题:

String hqlQueryString = query.unwrap(org.hibernate.Query.class).getQueryString();
ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
SessionImplementor hibernateSession = entityManager.unwrap(SessionImplementor.class);
QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator("", hqlQueryString, java.util.Collections.EMPTY_MAP, hibernateSession.getFactory(), null);
queryTranslator.compile(java.util.Collections.EMPTY_MAP, false);
String sqlQueryString = queryTranslator.getSQLString();

【讨论】:

今天这对我有用...似乎有这个问题的几个版本飞来飞去...【参考方案6】:

休眠类型

从 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 (HQL) 查询中获取 SQL 语句

假设我们有以下 JPQL (HQL) 查询:

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)
]

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

【讨论】:

【参考方案7】:

您可以使用 unwrap 方法获取查询。

String queryString = query.unwrap(org.hibernate.Query.class).getQueryString();

【讨论】:

哪个版本的hibernate有一个带有unwrap方法的查询? java6持久化api的一部分docs.oracle.com/javaee/6/api/javax/persistence/Query.html 这仅适用于获取 HQL 查询而不是 SQL 查询

以上是关于如何以编程方式将 HQL 转换为 SQL 查询(不记录)的主要内容,如果未能解决你的问题,请参考以下文章

HQL

HQL

hql 语法详解

HQL语句简单介绍

HQL 是什么意思?

如何以编程方式将 LINQ 查询转换为正确描述 linq 表达式的可读英文文本?