【问题标题】:How to get SQL from Hibernate Criteria API (*not* for logging)如何从 Hibernate Criteria API 获取 SQL(*not* 用于日志记录)
【发布时间】:2010-10-07 22:57:41
【问题描述】:

有没有办法从 Hibernate Criteria 获取(待生成的)SQL?

理想情况下,我会有类似的东西:

Criteria criteria = session.createCriteria(Operator.class);

... build up the criteria ...
... and then do something like ...

String sql = criteria.toSql()

(But this of course does not exist)

然后的想法是使用 SQL 作为一个巨大的 'MINUS' 查询的一部分(我需要找到 2 个相同模式之间的差异 - 结构相同,而不是数据 - 并且 Hibernate 不支持 MINUS)

(顺便说一句,我知道我可以从日志文件中检查 SQL)

【问题讨论】:

    标签: java sql hibernate criteria


    【解决方案1】:

    这是获取 SQL 的“另一种”方式:

    CriteriaImpl criteriaImpl = (CriteriaImpl)criteria;
    SessionImplementor session = criteriaImpl.getSession();
    SessionFactoryImplementor factory = session.getFactory();
    CriteriaQueryTranslator translator=new CriteriaQueryTranslator(factory,criteriaImpl,criteriaImpl.getEntityOrClassName(),CriteriaQueryTranslator.ROOT_SQL_ALIAS);
    String[] implementors = factory.getImplementors( criteriaImpl.getEntityOrClassName() );
    
    CriteriaJoinWalker walker = new CriteriaJoinWalker((OuterJoinLoadable)factory.getEntityPersister(implementors[0]), 
                            translator,
                            factory, 
                            criteriaImpl, 
                            criteriaImpl.getEntityOrClassName(), 
                            session.getLoadQueryInfluencers()   );
    
    String sql=walker.getSQLString();
    

    【讨论】:

    • 我尝试了您的解决方案,除了一件事外,它的效果很好。当我的标准具有criteria.setMaxResults(n) 时,它不能正确打印出来。它没有在生成的语句中满足该要求。你知道为什么吗?
    • 谢谢,帮助我调试了我必须处理的应用程序并立即找到错误。
    • 不错的答案。我已将其调整为在单行中运行,因此可以在调试会话中轻松运行或添加到观察列表等。请参阅下面的答案:stackoverflow.com/questions/554481#46788621
    • 我的实体关联条件查询与另一个表有 oneTomany 关系。所以 CriteriaQuery.list() 结果是一个联合结果。但是转换后的sql字符串不包含任何加入?
    【解决方案2】:

    我使用 Spring AOP 完成了类似的操作,因此我可以获取应用程序中运行的任何查询的 sql、参数、错误和执行时间,无论它是 HQL、Criteria 还是本机 SQL。

    这显然是脆弱的、不安全的,可能会随着 Hibernate 等的变化而中断,但它说明了获取 SQL 是可能的:

    CriteriaImpl c = (CriteriaImpl)query;
    SessionImpl s = (SessionImpl)c.getSession();
    SessionFactoryImplementor factory = (SessionFactoryImplementor)s.getSessionFactory();
    String[] implementors = factory.getImplementors( c.getEntityOrClassName() );
    CriteriaLoader loader = new CriteriaLoader((OuterJoinLoadable)factory.getEntityPersister(implementors[0]),
        factory, c, implementors[0], s.getEnabledFilters());
    Field f = OuterJoinLoader.class.getDeclaredField("sql");
    f.setAccessible(true);
    String sql = (String)f.get(loader);
    

    用 try/catch 封装整个东西,使用风险自负。

    【讨论】:

    • 暂时将休眠日志重定向到字符串不是更便携吗?
    • 可能,但如果多个线程同时执行 SQL,则可能很难确定哪些日志消息与您尝试捕获的 SQL 一起出现。使用 onPrepareStatement 的拦截器也会为您获取 SQL,但 OP 要求提供一种方法来获取给定 Criteria 对象的 SQL。
    • 有没有办法把SQL查询的参数也打印出来?
    • 这是我获取参数的方法:gist.github.com/bdeterling/5563683。我已经有大约 4 年没有重新访问它了。
    • 如果你使用的是带有 JPA 的 Hibernate 5.x,并且有一个 javax.persistence.Query 实例,这可以工作:return new org.hibernate.engine.jdbc.internal.BasicFormatterImpl().format (query.unwrap(org.hibernate.query.Query.class).getQueryString());
    【解决方案3】:

    对于那些使用 NHibernate 的人,这是 [ram] 代码的一个端口

    public static string GenerateSQL(ICriteria criteria)
        {
            NHibernate.Impl.CriteriaImpl criteriaImpl = (NHibernate.Impl.CriteriaImpl)criteria;
            NHibernate.Engine.ISessionImplementor session = criteriaImpl.Session;
            NHibernate.Engine.ISessionFactoryImplementor factory = session.Factory;
    
            NHibernate.Loader.Criteria.CriteriaQueryTranslator translator = 
                new NHibernate.Loader.Criteria.CriteriaQueryTranslator(
                    factory, 
                    criteriaImpl, 
                    criteriaImpl.EntityOrClassName, 
                    NHibernate.Loader.Criteria.CriteriaQueryTranslator.RootSqlAlias);
    
            String[] implementors = factory.GetImplementors(criteriaImpl.EntityOrClassName);
    
            NHibernate.Loader.Criteria.CriteriaJoinWalker walker = new NHibernate.Loader.Criteria.CriteriaJoinWalker(
                (NHibernate.Persister.Entity.IOuterJoinLoadable)factory.GetEntityPersister(implementors[0]),
                                    translator,
                                    factory,
                                    criteriaImpl,
                                    criteriaImpl.EntityOrClassName,
                                    session.EnabledFilters);
    
            return walker.SqlString.ToString();
        }
    

    【讨论】:

    • 您知道如何获取该查询中的参数值吗??
    【解决方案4】:

    如果您使用的是 Hibernate 3.6,您可以使用已接受答案中的代码(由 Brian Deterling 提供)稍作修改:

      CriteriaImpl c = (CriteriaImpl) criteria;
      SessionImpl s = (SessionImpl) c.getSession();
      SessionFactoryImplementor factory = (SessionFactoryImplementor) s.getSessionFactory();
      String[] implementors = factory.getImplementors(c.getEntityOrClassName());
      LoadQueryInfluencers lqis = new LoadQueryInfluencers();
      CriteriaLoader loader = new CriteriaLoader((OuterJoinLoadable) factory.getEntityPersister(implementors[0]), factory, c, implementors[0], lqis);
      Field f = OuterJoinLoader.class.getDeclaredField("sql");
      f.setAccessible(true);
      String sql = (String) f.get(loader);
    

    【讨论】:

      【解决方案5】:

      如果您只想获取查询的某些部分,我喜欢这样:

      new CriteriaQueryTranslator(
          factory,
          executableCriteria,
          executableCriteria.getEntityOrClassName(), 
          CriteriaQueryTranslator.ROOT_SQL_ALIAS)
              .getWhereCondition();
      

      例如这样的:

      String where = new CriteriaQueryTranslator(
          factory,
          executableCriteria,
          executableCriteria.getEntityOrClassName(), 
          CriteriaQueryTranslator.ROOT_SQL_ALIAS)
              .getWhereCondition();
      
      String sql = "update my_table this_ set this_.status = 0 where " + where;
      

      【讨论】:

        【解决方案6】:

        这是我使用并为我工作的一种方法

        public static String toSql(Session session, Criteria criteria){
            String sql="";
            Object[] parameters = null;
            try{
                CriteriaImpl c = (CriteriaImpl) criteria;
                SessionImpl s = (SessionImpl)c.getSession();
                SessionFactoryImplementor factory = (SessionFactoryImplementor)s.getSessionFactory();
                String[] implementors = factory.getImplementors( c.getEntityOrClassName() );
                CriteriaLoader loader = new CriteriaLoader((OuterJoinLoadable)factory.getEntityPersister(implementors[0]), factory, c, implementors[0], s.getEnabledFilters());
                Field f = OuterJoinLoader.class.getDeclaredField("sql");
                f.setAccessible(true);
                sql = (String)f.get(loader);
                Field fp = CriteriaLoader.class.getDeclaredField("traslator");
                fp.setAccessible(true);
                CriteriaQueryTranslator translator = (CriteriaQueryTranslator) fp.get(loader);
                parameters = translator.getQueryParameters().getPositionalParameterValues();
            }
            catch(Exception e){
                throw new RuntimeException(e);
            }
            if (sql !=null){
                int fromPosition = sql.indexOf(" from ");
                sql = "SELECT * "+ sql.substring(fromPosition);
        
                if (parameters!=null && parameters.length>0){
                    for (Object val : parameters) {
                        String value="%";
                        if(val instanceof Boolean){
                            value = ((Boolean)val)?"1":"0";
                        }else if (val instanceof String){
                            value = "'"+val+"'";
                        }
                        sql = sql.replaceFirst("\\?", value);
                    }
                }
            }
            return sql.replaceAll("left outer join", "\nleft outer join").replace(" and ", "\nand ").replace(" on ", "\non ");
        }
        

        【讨论】:

        • 感谢此代码。然而,有一个小错字(“traslator”应该是“translator”)。当用@Michael 的答案替换 CriteriaLoader 构造函数调用时,它也适用于休眠 3.6+(用 4.1.9 测试)
        【解决方案7】:

        对于希望在单行中执行此操作的任何人(例如在显示/立即窗口、调试会话中的监视表达式或类似内容),以下将执行此操作并“漂亮地打印”SQL:

        new org.hibernate.jdbc.util.BasicFormatterImpl().format((new org.hibernate.loader.criteria.CriteriaJoinWalker((org.hibernate.persister.entity.OuterJoinLoadable)((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getEntityPersister(((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getImplementors(((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName())[0]),new org.hibernate.loader.criteria.CriteriaQueryTranslator(((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),((org.hibernate.impl.CriteriaImpl)crit),((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),org.hibernate.loader.criteria.CriteriaQueryTranslator.ROOT_SQL_ALIAS),((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),(org.hibernate.impl.CriteriaImpl)crit,((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),((org.hibernate.impl.CriteriaImpl)crit).getSession().getEnabledFilters())).getSQLString());
        

        ...或者这里有一个更容易阅读的版本:

        new org.hibernate.jdbc.util.BasicFormatterImpl().format(
          (new org.hibernate.loader.criteria.CriteriaJoinWalker(
             (org.hibernate.persister.entity.OuterJoinLoadable)
              ((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getEntityPersister(
                ((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory().getImplementors(
                  ((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName())[0]),
             new org.hibernate.loader.criteria.CriteriaQueryTranslator(
                  ((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),
                  ((org.hibernate.impl.CriteriaImpl)crit),
                  ((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),
                  org.hibernate.loader.criteria.CriteriaQueryTranslator.ROOT_SQL_ALIAS),
             ((org.hibernate.impl.CriteriaImpl)crit).getSession().getFactory(),
             (org.hibernate.impl.CriteriaImpl)crit,
             ((org.hibernate.impl.CriteriaImpl)crit).getEntityOrClassName(),
             ((org.hibernate.impl.CriteriaImpl)crit).getSession().getEnabledFilters()
           )
          ).getSQLString()
        );
        

        注意事项:

        1. 答案基于the solution posted by ramdane.i
        2. 它假定Criteria 对象被命名为crit如果名称不同,请搜索并替换
        3. 假设 Hibernate 版本晚于 3.3.2.GA 但早于 4.0,以便使用 BasicFormatterImpl 来“漂亮地打印”HQL。 如果使用不同的版本,请参阅this answer 了解如何修改。或者也许只是完全删除漂亮的印刷品,因为它只是“很高兴拥有”
        4. 它使用 getEnabledFilters 而不是 getLoadQueryInfluencers() 来实现向后兼容性,因为后者是在更高版本的 Hibernate (3.5???) 中引入的
        5. 如果查询是参数化的,它不会输出实际使用的参数值。

        【讨论】:

          【解决方案8】:

          此答案基于 user3715338 的答案(纠正了一个小的拼写错误),并与 Michael 对 Hibernate 3.6 的答案混合 - 基于 Brian Deterling 接受的答案。然后我用更多的类型替换了问号(对于 PostgreSQL):

          public static String toSql(Criteria criteria)
          {
              String sql = "";
              Object[] parameters = null;
              try
              {
                  CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
                  SessionImpl sessionImpl = (SessionImpl) criteriaImpl.getSession();
                  SessionFactoryImplementor factory = sessionImpl.getSessionFactory();
                  String[] implementors = factory.getImplementors(criteriaImpl.getEntityOrClassName());
                  OuterJoinLoadable persister = (OuterJoinLoadable) factory.getEntityPersister(implementors[0]);
                  LoadQueryInfluencers loadQueryInfluencers = new LoadQueryInfluencers();
                  CriteriaLoader loader = new CriteriaLoader(persister, factory,
                      criteriaImpl, implementors[0].toString(), loadQueryInfluencers);
                  Field f = OuterJoinLoader.class.getDeclaredField("sql");
                  f.setAccessible(true);
                  sql = (String) f.get(loader);
                  Field fp = CriteriaLoader.class.getDeclaredField("translator");
                  fp.setAccessible(true);
                  CriteriaQueryTranslator translator = (CriteriaQueryTranslator) fp.get(loader);
                  parameters = translator.getQueryParameters().getPositionalParameterValues();
              }
              catch (Exception e)
              {
                  throw new RuntimeException(e);
              }
              if (sql != null)
              {
                  int fromPosition = sql.indexOf(" from ");
                  sql = "\nSELECT * " + sql.substring(fromPosition);
          
                  if (parameters != null && parameters.length > 0)
                  {
                      for (Object val : parameters)
                      {
                          String value = "%";
                          if (val instanceof Boolean)
                          {
                              value = ((Boolean) val) ? "1" : "0";
                          }
                          else if (val instanceof String)
                          {
                              value = "'" + val + "'";
                          }
                          else if (val instanceof Number)
                          {
                              value = val.toString();
                          }
                          else if (val instanceof Class)
                          {
                              value = "'" + ((Class) val).getCanonicalName() + "'";
                          }
                          else if (val instanceof Date)
                          {
                              SimpleDateFormat sdf = new SimpleDateFormat(
                                  "yyyy-MM-dd HH:mm:ss.SSS");
                              value = "'" + sdf.format((Date) val) + "'";
                          }
                          else if (val instanceof Enum)
                          {
                              value = "" + ((Enum) val).ordinal();
                          }
                          else
                          {
                              value = val.toString();
                          }
                          sql = sql.replaceFirst("\\?", value);
                      }
                  }
              }
              return sql.replaceAll("left outer join", "\nleft outer join").replaceAll(
                  " and ", "\nand ").replaceAll(" on ", "\non ").replaceAll("<>",
                  "!=").replaceAll("<", " < ").replaceAll(">", " > ");
          }
          

          【讨论】:

            猜你喜欢
            • 2015-04-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-07-30
            • 2021-01-17
            • 2011-10-18
            • 2017-06-26
            相关资源
            最近更新 更多