[java] How to get SQL from Hibernate Criteria API (*not* for logging)

Is there a way to get the (to-be-generated) SQL from a Hibernate Criteria?

Ideally, I would have something like:

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)

The idea would then be to use the SQL as part of a huge 'MINUS' query (I need to find the differences between 2 identical schemas - identical in structure, not in data - and the MINUS is not supported by Hibernate)

(BTW I know I can check the SQL from the log files)

This question is related to java sql hibernate criteria

The answer is


If you are using Hibernate 3.6 you can use the code in the accepted answer (provided by Brian Deterling) with slight modification:

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

I like this if you want to get just some parts of the query:

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

For instance something like this:

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;

Here's "another" way to get the 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();

For those using NHibernate, this is a port of [ram]'s code

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

For anyone wishing to do this in a single line (e.g in the Display/Immediate window, a watch expression or similar in a debug session), the following will do so and "pretty print" the 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());

...or here's an easier to read version:

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()
);

Notes:

  1. The answer is based on the solution posted by ramdane.i.
  2. It assumes the Criteria object is named crit. If named differently, do a search and replace.
  3. It assumes the Hibernate version is later than 3.3.2.GA but earlier than 4.0 in order to use BasicFormatterImpl to "pretty print" the HQL. If using a different version, see this answer for how to modify. Or perhaps just remove the pretty printing entirely as it's just a "nice to have".
  4. It's using getEnabledFilters rather than getLoadQueryInfluencers() for backwards compatibility since the latter was introduced in a later version of Hibernate (3.5???)
  5. It doesn't output the actual parameter values used if the query is parameterized.

Here is a method I used and worked for me

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

This answer is based on user3715338's answer (with a small spelling error corrected) and mixed with Michael's answer for Hibernate 3.6 - based on the accepted answer from Brian Deterling. I then extended it (for PostgreSQL) with a couple more types replacing the questionmarks:

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

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to sql

Passing multiple values for same variable in stored procedure SQL permissions for roles Generic XSLT Search and Replace template Access And/Or exclusions Pyspark: Filter dataframe based on multiple conditions Subtracting 1 day from a timestamp date PYODBC--Data source name not found and no default driver specified select rows in sql with latest date for each ID repeated multiple times ALTER TABLE DROP COLUMN failed because one or more objects access this column Create Local SQL Server database

Examples related to hibernate

Hibernate Error executing DDL via JDBC Statement How does spring.jpa.hibernate.ddl-auto property exactly work in Spring? Error creating bean with name 'entityManagerFactory' defined in class path resource : Invocation of init method failed JPA Hibernate Persistence exception [PersistenceUnit: default] Unable to build Hibernate SessionFactory Disable all Database related auto configuration in Spring Boot Unable to create requested service [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment] HikariCP - connection is not available Hibernate-sequence doesn't exist How to find distinct rows with field in list using JPA and Spring? Spring Data JPA and Exists query

Examples related to criteria

Hibernate Criteria Join with 3 Tables Hibernate Criteria Restrictions AND / OR combination How to properly express JPQL "join fetch" with "where" clause as JPA 2 CriteriaQuery? Hibernate Criteria for Dates Getting a count of rows in a datatable that meet certain criteria How to get SQL from Hibernate Criteria API (*not* for logging) How to get distinct results in hibernate with joins and row-based limiting (paging)? JPA and Hibernate - Criteria vs. JPQL or HQL Hibernate Query By Example and Projections