developer tip

Hibernate Criteria API에서 SQL을 얻는 방법 (로깅을위한 * 아님 *)

optionbox 2020. 11. 18. 08:56
반응형

Hibernate Criteria API에서 SQL을 얻는 방법 (로깅을위한 * 아님 *)


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)

그런 다음 아이디어는 거대한 'MINUS'쿼리의 일부로 SQL을 사용하는 것입니다 (구조가 동일하고 데이터가 아닌 두 개의 동일한 스키마 사이의 차이점을 찾아야하며 MINUS는 Hibernate에서 지원되지 않습니다)

(BTW 로그 파일에서 SQL을 확인할 수 있음을 알고 있습니다)


Spring AOP를 사용하여 이와 같은 작업을 수행하여 HQL, Criteria 또는 네이티브 SQL 여부에 관계없이 응용 프로그램에서 실행되는 모든 쿼리에 대한 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을 얻는 "또 다른"방법입니다.

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

한 줄 (예 : Display / Immediate 창, 디버그 세션의 조사 식 또는 이와 유사한)에서이 작업을 수행하려는 모든 사람을 위해 다음은 그렇게하고 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가 게시 한 솔루션을 기반으로 합니다 .
  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.

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

참고URL : https://stackoverflow.com/questions/554481/how-to-get-sql-from-hibernate-criteria-api-not-for-logging

반응형