本文想解决园子里大家都关心的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语句,获取全部记录和获取分页记录(含有总条数)两件事就都可以搞定.
觉得好的话,别忘了顶一下哦,呵呵. |