代码之家  ›  专栏  ›  技术社区  ›  Arnis Lapsa

nhibernate第二级lvl缓存,自定义查询,sqldialect

  •  3
  • Arnis Lapsa  · 技术社区  · 14 年前

    我有NH和FNH的后备箱版本。当我试图添加二级缓存时,nhibernate的某些部分忘记了所选的sqldialect。


    初始配置:

    var cfg = Fluently.Configure()
      .Database(MsSqlConfiguration.MsSql2008
        .ConnectionString(connectionString)
        .DefaultSchema("dbo")
        .UseReflectionOptimizer()    
      .Mappings(m => ................);
    

    有罪自定义查询:

    var sql = @"with Foo(col1,col2,col3)
                  as (select bla bla bla...)
                Select bla bla bla from Foo";
    
    list = Session.CreateSQLQuery(sql)
      .AddEntity("fizz", typeof(Fizz))
      .SomethingUnimportant();
    

    当我将配置更改为:

    var cfg = Fluently.Configure()
      .Database(MsSqlConfiguration.MsSql2008
        .ConnectionString(connectionString)
        .DefaultSchema("dbo")
         .UseReflectionOptimizer()
         .Cache(c=>c
           .UseQueryCache()
             .ProviderClass<HashtableCacheProvider>())
           .ShowSql())
         .Mappings(m => ................);
    

    查询抛出错误( WITH 在MSSQL2008中添加了以下条款:

    查询应以“select”或“select distinct”开头

    [不支持异常:查询应以'select'或'select distinct'开头] nhibernate.dialect.mssql2000dialect.getafterselectinsertpoint(sqlstring sql)+179 nhibernate.dialect.mssql2000dialect.getlimitstring(sqlstring querysqlstring,int32 offset,int32 limit)+119 nhibernate.dialect.mssql2005dialect.getlimitstring(sqlstring querysqlstring,int32 offset,int32 last)+127 nhibernate.loader.loader.preparequerycommand(queryparameters queryparameters,boolean scroll,isessionimplementor会话)+725 nhibernate.loader.loader.doquery(ISessionImplementor会话,QueryParameters QueryParameters,布尔返回代理)+352 nhibernate.loader.loader.doQueryAndInitializeNonLazyCollections(ISessionImplementor会话,QueryParameters QueryParameters,布尔返回代理)+114 nhibernate.loader.loader.dolist(ISessionImplementor会话,queryParameters queryParameters)+205


    你知道到底是什么让NHibernate困惑吗?怎么解决?


    有罪的NHibernate代码(用NHibernate/dialect/mssql200dialect.cs表示):

    private static int GetAfterSelectInsertPoint(SqlString sql)
    {
      if (sql.StartsWithCaseInsensitive("select distinct"))
      {
        return 15;
      }
      else if (sql.StartsWithCaseInsensitive("select"))
      {
        return 6;
      }
      throw new NotSupportedException
        ("The query should start with 'SELECT' or 'SELECT DISTINCT'");
      }
    }
    

    看起来 .SetMaxResults(123) 导致了这个。幸运的是,我可以解开这个查询。

    希望这能解决这个问题。

    7 回复  |  直到 10 年前
        1
  •  3
  •   Sandor Drieënhuizen    14 年前

    我也有类似的问题 SetMaxResults 也有帮助,但我需要分页),并发现以下nhibernate配置属性导致了此错误:

    <property name="use_sql_comments">true</property>
    

    这肯定是个虫子,因为 GetAfterSelectInsertPoint 方法不考虑SQL查询前面可能有SQL注释。

    只是设置 use_sql_comments 属性到 false 问题就消失了。

        2
  •  4
  •   cbp    13 年前

    我使用alkampfer的解决方案修复了这个bug,但是我创建了自己的sql方言,而不是直接修补nhibernate源代码:

    public class Sql2008DialectWithBugFixes : MsSql2008Dialect
    {
        public override SqlString GetLimitString(SqlString querySqlString, int offset, int last)
        {
            if (offset == 0)
            {
                return querySqlString.Insert(GetAfterSelectInsertPoint(querySqlString), " top " + last);
            }
    
            return base.GetLimitString(querySqlString, offset, last);
        }
    
        private static int GetAfterSelectInsertPoint(SqlString sql)
        {
            Int32 selectPosition;
    
            if ((selectPosition = sql.IndexOfCaseInsensitive("select distinct")) >= 0)
            {
                return selectPosition + 15; // "select distinct".Length;
    
            }
            if ((selectPosition = sql.IndexOfCaseInsensitive("select")) >= 0)
            {
                return selectPosition + 6; // "select".Length;
            }
    
            throw new NotSupportedException("The query should start with 'SELECT' or 'SELECT DISTINCT'");
        }
    }
    
        3
  •  2
  •   Thierry    14 年前

    只是在使用带有with子句的类似查询时遇到了相同的问题。

    不幸的是,我的查询用分页填充了一个网格,所以我必须保留setmaxresults。

    我的解决方案是使用派生表重写:

    var sql = @"with Foo(col1,col2,col3)
                  as (select x1, x2, x3 from x join y blabla)
                Select col1, col2, col3 from Foo
                join B on B.col1 = Foo.col1";
    

    变成

    var sql = @"Select col1, col2, col3 from 
               (select x1 as col1, x2 as col2, x3 as col3 
                from x join y blabla) as Foo
               join B on B.col1 = Foo.col1";
    

    只允许nhibernate在“select”字符串后面插入“top x”字符串(从开头开始6个字符)。无可奉告:(

    T

        4
  •  2
  •   Alkampfer    13 年前

    sandor告诉我们,在用于在查询中找到插入top子句(getAfterSelectInsertPoint)的位置的例程中似乎有一些奇怪的错误。你可以直接在nh source中修复它(实际上我已经修补了我在项目中使用的2.1版本,你可以 find details here )因此,如果您确实需要启用带有use-sql-comments的注释,您可以:)

        5
  •  2
  •   neoscribe    12 年前

    当从1.2升级到3.2时,我遇到了这个问题(我知道,大跳跃是吗?).

    在我的例子中,问题是hql中的select语句前面有一个前导空格,例如string hql=“select”…

    使用sql2005方言时,这会因“system.notsupportedexception:查询应以“select”…”消息开头而崩溃。

    解决办法是

    1. 创建一个失败的单元测试,一个好的测试驱动开发人员 应该:)
    2. 从“select…”语句中删除前导空格
    3. 生成并运行单元测试
        6
  •  0
  •   Arnis Lapsa    14 年前

    正如我所预测的-解除绑定select是可接受的解决方案。

    删除 SetMaxResults 而且有效。

        7
  •  0
  •   Sean    10 年前

    我们在升级到NHibernate3.3版本时遇到了这个问题,但原因不同…空白。我们有很多这样的sql字符串:

    var sql = @"
    select col1 from MyTable";
    

    或:

    var sql = @" select col1 from My Table";
    

    这导致了“查询应该以‘select’或‘select distinct’开头”错误,因为nhibernate在验证之前不会修剪字符串。

    我们创建了一个新的方言,它首先修剪字符串以绕过以下问题:

    public class Sql2008DialectCustom : MsSql2008Dialect
    {
      public override SqlString GetLimitString(SqlString queryString, SqlString offset, SqlString limit)
      {
        var trimmedQueryString = queryString.Trim();
        return base.GetLimitString(trimmedQueryString, offset, limit);
      }
    }
    
    推荐文章