如何从Hibernate Criteria API获取SQL(* not *用于日志logging)

有没有一种简单的方法从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'查询的一部分(我需要find2个相同的模式之间的差异 – 结构相同,而不是在数据 – 和hibernate不支持的MINUS)

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

我已经使用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); 

把所有的东西都包裹起来,然后使用,风险自负。

这是“另一种”获得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(); 

对于那些使用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(); } 

如果您使用的是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); 

我喜欢这个,如果你只想得到一些查询的一部分:

 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; 

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

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

这个答案是基于user3715338的答案(与一个小的拼写错误纠正),并混合迈克尔的回答为hibernate3.6 – 基于从Brian Deterling接受的答案。 然后我扩展它(用于PostgreSQL)与更多typesreplace问号:

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

对于希望单行执行的人(例如在Display / Immediate窗口中,在debugging会话中使用一个监视expression式或类似的expression式),下面的代码将会这样做,并“漂亮地打印”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. 答案是基于ramdane.i发布的解决scheme 。
  2. 它假定Criteria对象被命名为crit如果命名不同,请执行search和replace
  3. 它假定Hibernate版本比3.3.2.GA晚,但是早于4.0,以便使用BasicFormatterImpl来“漂亮地打印”HQL。 如果使用不同的版本,请参阅此答案以了解如何修改。 或者,也许只是完全删除漂亮的打印,因为它只是一个“很高兴”
  4. 它使用getEnabledFilters而不是getLoadQueryInfluencers()来向后兼容,因为后者是在Hibernate(3.5 ???)的后续版本中引入的。
  5. 如果查询被参数化,它不会输出使用的实际参数值。