NHibernate 2nd lvl 缓存、自定义查询、sqldialect
-
20-09-2019 - |
题
我买的是 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”开头
[不支持异常:The query should start with 'SELECT' or '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上次)+127 NHIBERNATE.LOADER.LOADER.PREPAREQUERYCOMMAND(QUERYPARAMETERS) ,Queryparameter ) +352 NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) +114 NHibernate.Loader.Loader.DoList(ISessionImplementor session, 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)
导致这种情况。幸运的是,我可以解除该查询的绑定。
希望这能解决这个问题。
解决方案
我有一个类似的问题(删除 SetMaxResults
也有帮助,但我需要分页)并发现以下 NHibernate 配置属性导致了此错误:
<property name="use_sql_comments">true</property>
这肯定是一个错误,因为 GetAfterSelectInsertPoint
方法没有考虑到 SQL 注释可能会添加到 SQL 查询之前。
只需设置 use_sql_comments
财产给 false
问题就消失了。
其他提示
我使用 Alkampfer 的解决方案修复了该错误,但我创建了自己的 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'");
}
}
刚刚使用具有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 个字符)...没有意见 :(
时间
正如 Sandor 所说,用于在查询中查找插入 TOP 子句 (GetAfterSelectInsertPoint ) 的位置的例程中似乎存在一些奇怪的错误。你可以直接在nh源中修复它(我实际上修补了我在项目中使用的2.1版本,你可以 在这里找到详细信息)。因此,如果您绝对需要使用 use_sql_comments 启用注释,您可以:)
我从 1.2 升级到 3.2 时遇到了这个问题(我知道,大跳跃是吗?)。
我的问题是 hql 中的 select 语句前面有一个前导空格,例如字符串 hql =“选择”...
对于 SQL2005 Dialect,这会崩溃并出现“System.NotSupportedException:查询应以“SELECT”消息开头。
解决办法是
- 创建一个失败的单元测试,良好的测试驱动的开发人员应:)
- 从“select...”语句中删除前导空格
- 构建并运行单元测试
正如我预测的那样 - 无界选择是可以接受的解决方法。
已删除 SetMaxResults
它有效。
我们在升级到 NHibernate 3.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);
}
}