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

原文地址:https://www.cnblogs.com/goldarch/p/1731144.html