我怎样才能让 NHibernate 只生成 SQL 而不执行它?

Posted

技术标签:

【中文标题】我怎样才能让 NHibernate 只生成 SQL 而不执行它?【英文标题】:How can I have NHibernate only generate the SQL without executing it? 【发布时间】:2012-05-29 02:06:04 【问题描述】:

我知道如何在运行时使用show_sql 配置选项将 SQL 记录到 log4net/NLog/trace 窗口。

我正在寻找一种将Query<T>() 提供给NHibernate 检索生成的SQL 的方法。

我查看了 Persister 类、驱动程序、不同的拦截器和事件。有很多地方要看,即使缩小搜索范围也会有很大帮助。

【问题讨论】:

我想要完成的是一个穷人的分析器。我只想知道一个特定的 linq 查询将如何从一段测试代码中评估。 【参考方案1】:

您可以通过以下方法获得生成的sql查询而不执行:

对于 NHibernate.Linq 查询:

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;

对于条件查询:

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

对于 QueryOver 查询:

public String GetGeneratedSql(IQueryOver queryOver)

    return GetGeneratedSql(queryOver.UnderlyingCriteria);

对于 Hql 查询:

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;

【讨论】:

@Gerard 我赞成您非常有用的答案。您是否知道插入 - 更新是否可行?见***.com/questions/10786934/…谢谢! 非常非常有用的答案。对于条件查询,sqlstring 包含 ?为位置参数。我能够使用 loader.Translator.GetQueryParameters().PositionalParameterValues 获取它们,并使用 out 参数将其传递回调用者。 有什么方法可以强制GetGeneratedSql(ICriteria criteria) 返回带有命名参数而不是占位符的查询 (?)? 有什么办法可以扭转这种局面吗? ,我的意思是生成的 sql 字符串到 QueryOver 或实体? 对于 Nhibernate 4.0.4 使用 CreateQueryTranslators 是否有更新?此方法发生了重大更改,签名现在不同了。【参考方案2】:

对于 NHibernate 5.2,如果您想查看为查询准备的实际 DbCommand(因此您可以检查 cmd.CommandText 中的 SQL 和 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;
    

【讨论】:

【参考方案3】:

基于 NHibernate 3.4 版本的 linq 表达式的方法是:

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.Key, nhLinqExpression, null, false,
                                                                sessionImp.EnabledFilters, sessionImp.Factory);

         var sql = translators.First().SQLString;
         var formamttedSql = FormatStyle.Basic.Formatter.Format(sql);
         int i = 0;
         var map = ExpressionParameterVisitor.Visit(queryable.Expression, sessionImp.Factory).ToArray();
         formamttedSql = Regex.Replace(formamttedSql, @"\?", m => map[i++].Key.ToString().Replace('"', '\''));

         return formamttedSql;
      

【讨论】:

有没有办法将 sql 字符串转换为 hql 或 queryover?【参考方案4】:

这里是如何使用 NH 5.2 从 Hql 生成 Sql(NH 4.0.4 中出现了一个重大变化,这使得 Hql 成为投票最多的解决方案的一部分):

public string HqlToSql(string hql, ISession session)

    var sessionImp = (ISessionImplementor)session;
    var translatorFactory = new ASTQueryTranslatorFactory();
    var translators = translatorFactory.CreateQueryTranslators(new NHibernate.Hql.StringQueryExpression(hql),
         null, false, sessionImp.EnabledFilters, sessionImp.Factory);
    var hqlSqlGenerator = new HqlSqlGenerator(((QueryTranslatorImpl)translators[0]).SqlAST, sessionImp.Factory);
    hqlSqlGenerator.Generate();
    return hqlSqlGenerator.Sql.ToString();

【讨论】:

以上是关于我怎样才能让 NHibernate 只生成 SQL 而不执行它?的主要内容,如果未能解决你的问题,请参考以下文章

如果生成警告,我怎样才能让 Perl 死掉?

为啥 chr() 返回一个十六进制值,我怎样才能让它只返回字符 [重复]

NHibernate 似乎没有批量插入 PostgreSQL

插入 nhibernate sql server 后无法检索生成的 id

NHibernate bug sql 生成器

谷歌云视觉 DOCUMENT_TEXT_DETECTION 语言提示 - 我怎样才能让武力只使用一种语言?