SQLite 使用NHibernate分页的BUG
今天因为sqlite分页的原因,浪费了几个小时!
sqlite从第三个分页开始,只显示第二页的数据!
在检查自己代码无误的情况下,开始怀疑NHibernate处理sqlite处理分页有问题!
把数据库换成ms sql,发现分页没有问题!
还好,这时候找了一篇文章,解答了我的疑惑!
不同的是:我用的是NHibernate-2.0.1.GA正式版!
探索的路,真的很难!基于这个原因!试试用NHibernate2.1GA版本,反正spring.net 1.3已经支持了。
http://3rgb.com/entry/bug_of_NHibernate_with_SQLite
原文如下:
-
先说明,我用的是NHibernate2.0.1-GA版本,测试版的,并且以下出现的问题已经被BUG TRACKER记录并修正,但是NH站上提供的BIN也好,源SOURCE也好,都还没有更新。
具体情况:N3C用的是SQLite3,然后使用上述的NHibernate进行数据库操作,在进行分页的时候,只能正确分出第一页和第二页,从第三页开始往后,只显示第二页的内容。
SQLite中分页取数据的方面是SQL语句后面加limit startRow,offset,如果是第一页,直接就是limit offset;比如我每页显示10条,正常情况下是,第一页limit 10,第二页limit 10,10,第三页limit 20,10,第四页limit 30,10。。。但是在上述版本的NHibernate所产生的SQL语句中,从第三页开始变成limit 10,20,第四页limit 10,30如此类推;原因在于NH在组装SQL语句的时候,把start和offset给弄反了。而在输出数据时,由于设置了offset为10,后面的情况就变成了,取从第10条记录开始的20,30,40,50。。。。第记录,然后再只返回前10条;这样一来,越往后分页效率越差,数据库压力越大不说,从第三页开始往后,我们只能看到第二页的10条记录了。
所以,要么等NH官方修复这个BUG,要么就自己改源SOURCE自己编译个新的DLL吧。
具体修改方法:
Index: SQLiteDialect.cs
===================================================================
--- SQLiteDialect.cs (revision 3862)
+++ SQLiteDialect.cs (working copy)
@@ -179,14 +179,15 @@
SqlStringBuilder pagingBuilder = new SqlStringBuilder();
pagingBuilder.Add(querySqlString);
pagingBuilder.Add(" limit ");
- pagingBuilder.Add(limit.ToString());
-
+
if (offset > 0)
{
+ pagingBuilder.Add(offset.ToString());
pagingBuilder.Add(", ");
- pagingBuilder.Add(offset.ToString());
}+ pagingBuilder.Add(limit.ToString());
+
return pagingBuilder.ToSqlString();
}
}上面的代码来自于http://jira.nhibernate.org/secure/attachment/12560/SQLiteDialect-FixPaging.patch,其中前面标“-”号的意思是要删掉的行,加“+”号的是新加的代码。修改之后,就编译一个新的NHibernate.dll出来吧。
【后记】
把 nhibernate转换成2.1GA版本后,分布bug消失。
原有代码只改动了一处:
#region 1.2时可用
//recordCount = Convert.ToInt32(criteria.SetProjection(Projections.RowCount()).UniqueResult());
//消除,便于重用
//以下在1.3版本报错!
//criteria.SetProjection(null);
#endregion
#region 1.3版本对:SetProjection有改变
// * ICriteria.SetProjection now takes a params array of projections, instead of a single projection
//Only a breaking change if you are implementing ICriteria, there is full source code compatability
//Projections.ProjectionList():create a new projection list,每次查询都是新建一个ProjectionList
//这样就不存在要消除Projection设置的问题!
recordCount = Convert.ToInt32(criteria.SetProjection(Projections.ProjectionList()
.Add(Projections.RowCount())).UniqueResult());
#endregion