2009-02-16 244 views
56

是否有一種簡單的方法從Hibernate Criteria獲取(將要生成的)sql?如何從Hibernate Criteria API獲取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」查詢(我需要找到兩個相同的模式之間的差異的一部分 - 在相同結構,而不是在數據 - 和負不受休眠支持)

(順便說一句,我知道我可以從日誌文件檢查SQL)

回答

33

我做了這樣的事使用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和使用整個事情風險自負。

+0

將hibernate日誌暫時重定向到字符串會不會更具可移植性? – 2011-08-15 13:28:12

+0

可能,但是如果多個線程同時執行SQL,可能很難確定哪條日誌消息與您嘗試捕獲的SQL一起運行。使用onPrepareStatement的攔截器也會讓你獲得SQL,但是OP要求一種獲得給定Criteria對象的SQL的方法。 – 2011-08-15 21:26:01

+1

有沒有辦法可以打印出SQL查詢的參數? – JRR 2013-05-11 12:51:06

35

這裏的 「其他」 的方式來獲得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(); 
10

對於使用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(); 
    } 
7

如果您在使用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); 
4

我,如果你W¯¯喜歡這個螞蟻得到公正查詢的某些部分:

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; 
3

這裏是我以前和我

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 "); 
} 
0

工作方法這個答案基於user3715338的回答(修正了一個小的拼寫錯誤),並與Michael對Hibernate 3.6的回答混合在一起 - 基於Brian Deterling接受的答案。我然後用一對夫婦更多種類型的替換questionmarks延長它(對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(">", " > "); 
} 
0

對於希望這樣做在單個線(任何人都在顯示/立即窗口,手錶表達或以類似的例如調試會話),以下將這樣做和「漂亮打印」的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()爲了向後兼容,因爲後者是在休眠(3.5 ???)的更高版本引入
  5. 它不輸出用於如果該查詢是參數化的實際參數值。
相關問題