[小把戏] 之IBatis.Net系统分页问题的解决

  本文想解决园子里大家都关心的Ibatis.Net分页的实现问题。其实我也找了Ibatis.Net分页的解决方案,园子里有一些,奈何没有发现我想要的方式,大都的策略是调用两次SQL Statement,第一次,根据条件查询总记录条数,第二次,根据条件查询从第N行到M行的数据集合,不是这种不能满足条件,恰恰相反,这种写法,完全可以满足现有项目的需求,也很具备SQL调优空间,但有个的弊端,是通过一个查询写几遍。很难复用。感觉很是别扭,我看过在查询较多的系统里,到处充斥这分页的SQL语句,另外一点,本来我们码农们只要关心实现获取业务数据就行了,每次在实现功能的基础上,还要再加上分页的SQL语句,使原本的可能就复杂的语句,变得更加复杂和难以理解。

  为此,我特地Reflect看了Ibatis.Net的源码。根据他的处理逻辑扩展了SqlMapper的接口,增加 IList<T> QueryForListWithPage<T>这种功能。本想通过注入的方式实现的,看过Java的筒子们曾用注入的方式搞定过。可惜Ibatis.Net没有发现这种方式(或许我才疏学浅,没有找到)。貌似MyBatis已经支持。但没有实践过。本文代码只针对Ibatis 1.62版本和使用MSSQL数据库。如果感觉此思路还不错,我的源码是共享滴,完全可以下载源码,改造成你想要的其它方式。

  至于分页的SQL,个人偏好row_number这种方法,其它的如分页存储过程,TOP方式就不费笔墨了。

  话多无益。直接上码  

 //获取学生列表(使用配置GetStudentList的sql,按照id升序排序获取从第八十个到第一百学生)
 var list = mapper.QueryForListWithPage<T>("GetStudentList", paras, "id asc", 80, 100, ref count);

   那么具体实现怎么写呢,我采用c#的扩展方法,扩展了Ibatis.Net标配的SqlMapper功能,看起来类似Ibatis.Net原生态的功能,但又没有破坏Ibatis源码,这种插件式,不会给原有的Ibatis引入任何的Bug. 

    public static class SqlMapperExtension
    {
        private const string PageSql =
            "with cte as( select id0=row_number() over(order by {0}),* from  ({1}) as cte1) select * from cte where id0 between @beginNo and @endNo";

        private const string CountSql = "select count(*) {0}";

        /// <summary>
        /// 查询分页
        /// </summary>
        /// <typeparam name="T">泛型</typeparam>
        /// <param name="mapper">mapper</param>
        /// <param name="tag">SQL Statement的id</param>
        /// <param name="paramObject">参数</param>
        /// <param name="orderby">查询条件,必须确保数据库中有这一列</param>
        /// <param name="beginNo">开始行数</param>
        /// <param name="endNo">结束行数</param>
        /// <param name="totalCount">总条数</param>
        /// <returns>查询结果</returns>
        public static IList<T> QueryForListWithPage<T>(this ISqlMapper mapper, string tag, object paramObject,string orderby, int beginNo, int endNo, ref int totalCount)
        {
            bool flag = false;
            ISqlMapSession sqlMapSession = mapper.LocalSession;
            if (sqlMapSession == null)
            {
                sqlMapSession = mapper.CreateSqlMapSession();
                flag = true;
            }
            try
            {
                IMappedStatement mappedStatement = mapper.GetMappedStatement(tag);
                IStatement statement = mappedStatement.Statement;
                RequestScope request = statement.Sql.GetRequestScope(mappedStatement, paramObject, sqlMapSession);
                string statementsql = request.PreparedStatement.PreparedSql;
                string cmdPageSql = string.Format(PageSql, orderby, statementsql);
                string cmdCountSql = string.Format(CountSql,statementsql.Substring(statementsql.ToLower().IndexOf("from")));

                request.PreparedStatement.PreparedSql = cmdPageSql;
                request.IDbCommand = new DbCommandDecorator(sqlMapSession.CreateCommand(statement.CommandType), request);
                ApplyParameterMap(sqlMapSession, request.IDbCommand, request, statement, paramObject);
                totalCount = GetCount(request, sqlMapSession, cmdCountSql);
                request.IDbCommand.CommandText = request.PreparedStatement.PreparedSql;
                AddCommandParameters(beginNo, endNo, request);
                IList<T> result = RunQueryForList<T>(statement, request, sqlMapSession, paramObject, null, null);
                return result;
            }
            finally
            {
                if (flag)
                {
                    sqlMapSession.CloseConnection();
                }
            }
        }
    }

   园友们若想直接拿来使用,可以下载附件文件,在引用Ibatis.Net的dll同时,在工程里再增加SqlMapperExtension这个类文件,就可以方便调用了。此方法在Ibatis.Net1.62版本下,使用MSSQL,已经通过测试验证。OK,如果你不想分页功能,那么就还是使用下面的方式。  

         var listall = mapper.QueryForList<T>(statementName, paras);

  这样你只需要在sqlmapper的xml里写一个SQL语句,获取全部记录和获取分页记录(含有总条数)两件事就都可以搞定.

  觉得好的话,别忘了顶一下哦,呵呵.

posted on 2012-05-28 15:52  围成  阅读(2584)  评论(10编辑  收藏  举报

导航