Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I have NHibernate only generate the SQL without executing it?

I know how to log the SQL to log4net/NLog/trace window at runtime with the show_sql configuration option.

What I'm looking for is a way to give a Query<T>() to NHibernate retrieve the generated SQL.

I've looked through the Persister class, the Drivers, different Interceptors and Events. There are so many places to look, even narrowing down my search would be of great help.

like image 688
hometoast Avatar asked May 22 '12 14:05

hometoast


2 Answers

You can get the generated sql queries without execution with the following methods:

For the NHibernate.Linq queries:

public String GetGeneratedSql(System.Linq.IQueryable queryable, ISession session)
{
    var sessionImp = (ISessionImplementor) session;
    var nhLinqExpression = new NhLinqExpression(queryable.Expression, sessionImp.Factory);
    var translatorFactory = new ASTQueryTranslatorFactory();
    var translators = translatorFactory.CreateQueryTranslators(nhLinqExpression, null, false, sessionImp.EnabledFilters, sessionImp.Factory);

    return translators[0].SQLString;
}

For Criteria queries:

public String GetGeneratedSql(ICriteria criteria)
{
    var criteriaImpl = (CriteriaImpl) criteria;
    var sessionImpl = (SessionImpl) criteriaImpl.Session;
    var factory = (SessionFactoryImpl) sessionImpl.SessionFactory;
    var implementors = factory.GetImplementors(criteriaImpl.EntityOrClassName);
    var loader = new CriteriaLoader((IOuterJoinLoadable) factory.GetEntityPersister(implementors[0]), factory, criteriaImpl, implementors[0], sessionImpl.EnabledFilters);

    return loader.SqlString.ToString();
}

For QueryOver queries:

public String GetGeneratedSql(IQueryOver queryOver)
{
    return GetGeneratedSql(queryOver.UnderlyingCriteria);
}

For Hql queries:

public String GetGeneratedSql(IQuery query, ISession session)
{
    var sessionImp = (ISessionImplementor)session;
    var translatorFactory = new ASTQueryTranslatorFactory();
    var translators = translatorFactory.CreateQueryTranslators(query.QueryString, null, false, sessionImp.EnabledFilters, sessionImp.Factory);

    return translators[0].SQLString;
}
like image 76
Gerard Avatar answered Oct 16 '22 05:10

Gerard


For NHibernate 5.2 in case you want to see actual DbCommand prepared for query (so you can check both SQL in cmd.CommandText and supplied parameters in cmd.Parameters):

//For LINQ
public IEnumerable<DbCommand> GetDbCommands<T>(IQueryable<T> query, ISession s)
{
    return GetDbCommands(LinqBatchItem.Create(query), s);
}

//For HQL
public IEnumerable<DbCommand> GetDbCommands(IQuery query, ISession s)
{
    return GetDbCommands(new QueryBatchItem<object>(query), s);
}

//For QueryOver
public IEnumerable<DbCommand> GetDbCommands(IQueryOver query, ISession s)
{
    return GetDbCommands(query.RootCriteria, s);
}

//For Criteria (needs to be called for root criteria)
public IEnumerable<DbCommand> GetDbCommands(ICriteria rootCriteria, ISession s)
{
    return GetDbCommands(new CriteriaBatchItem<object>(query), s);
}

//Adapted from Loader.PrepareQueryCommand
private static IEnumerable<DbCommand> GetDbCommands(IQueryBatchItem item, ISession s)
{
    var si = s.GetSessionImplementation();
    item.Init(si);
    var commands = item.GetCommands();
    foreach (var sqlCommand in commands)
    {
        //If you don't need fully prepared command sqlCommand.Query contains SQL returned by accepted answer
        var sqlString = sqlCommand.Query;
        sqlCommand.ResetParametersIndexesForTheCommand(0);
        var command = si.Batcher.PrepareQueryCommand(System.Data.CommandType.Text, sqlString, sqlCommand.ParameterTypes);
        RowSelection selection = sqlCommand.QueryParameters.RowSelection;
        if (selection != null && selection.Timeout != RowSelection.NoValue)
        {
            command.CommandTimeout = selection.Timeout;
        }

        sqlCommand.Bind(command, si);

        IDriver driver = si.Factory.ConnectionProvider.Driver;
        driver.RemoveUnusedCommandParameters(command, sqlString);
        driver.ExpandQueryParameters(command, sqlString, sqlCommand.ParameterTypes);
        yield return command;
    }
}
like image 3
Roman Artiukhin Avatar answered Oct 16 '22 06:10

Roman Artiukhin