OEA ORM中的分页支持

    本篇博客主要描述分页的常见技术方案,以及在 OEA 框架中的分页的应用及实现原理。

分页的几种方案

    分页是解决大数据量显示的有效方法。根据分页技术应用的位置不同,大致可以把分页分为以下几种:

  • 界面层分页

    界面层的分页,类似于界面的虚拟化技术,是只显示需要的数据的一种技术。OEA 的 WPF 界面中目前已经实现了 UI 虚拟化,所以不再实现界面层分页。

优点:

* 简单。许多控件都支持在界面层直接进行分页。

* 换页时,响应快。(在 C/S 结构下使用这种方案,数据都已经到达客户端,所以在分页时不需要额外的数据查询,响应速度较快。)

缺点:

* 不用于太大的数据分页。由于没有减少网络传输,首次加载时较慢,需要把所有数据都传输到客户端。

  • 实体层分页

    在实体层进行分页操作的方案,很少会被使用。它是把查询出来的数据,在服务器端都转换为实体,然后再找到具体页的实体数据,其它的数据则直接丢弃。

优点:

* 减少了首次的网络传输,对于客户端而言,调用的是分页的 API。

* 简单。

* 通用性强,与数据库无关,方案可以跨多种数据库。

* 统计总行数不需要发起二次查询。

缺点:

* 占用内存,依然不能用于太大的数据分页。

  • 数据层分页

    这种方案一般使用 IDataReader 实现。查询的 SQL 依然是查询所有的数据,但是在对查询出的 IDataReader 进行遍历读取每一行时,只读取对应页的数据,其它页的数据则忽略。同时,遍历到记录集的最后一行,即可获得数据的总行数。

优点:

* 不占用大量内存。只把需要的数据读取到内存中。

* 简单。

* 通用性强,与数据库无关,方案可以跨多种数据库。

* 统计总行数不需要发起二次查询。

缺点:

* 查询的 SQL 会查询很大的一张表。遍历依然需要耗费一定的时间。

  • 数据库分页

    分页的最终方案,自然是在数据库中进行分页。这也是大多数情况会选用的方案。

优点:

* 性能最好。速度快、占用内存小。

* 统计行数时,往往需要重新发起查询。

缺点:

* 对于框架开发而言,要生成分页相关的 SQL,较麻烦。

* 方案与特定数据库相关。通用性低。

虽然提到了这几种不同层面的分页方案。但是对应应用开发而言,数据库的分页是最常用的。只是在做 OEA 框架开发时,由于要支持多种数据库,所以需要在合适时采用不同的方案。同时,也不会考虑使用存储过程来辅助分页。

OEA 分页 - 应用层接口

    在说明 OEA 的分页前。先介绍一个 PagingInfo 类型(老版本中,该类名为 PagerInfo),这关系到整个分页方案的接口设计:

图1 位于 Common(原 hxy)程序集中的 PagingInfo 类型

图2 PagingInfo 类型接口

    在查询数据时,我们指定了查询的具体页码 PageIndex、一页所含数据行数 PageSize,就可以把该页的数据显示在界面上了。但是,在分页时,往往要在界面中显示一个分页脚,用于显示当前页号、所有页数。所以在进行查询的同时,往往还需要对结果集中所有数据的总行数进行统计,并把之与查询出的实体列表数据一同返回。所以,我为 PagingInfo 添加了额外的两个属性,IsNeedCount、TotalCount,当 IsNeedCount 被设置为真时,框架在数据层进行查询时,会把统计出来的总行数赋值给 TotalCount。

OEA 分页 - 使用方法

    下面以分页查询所有数据为例,简单说明如何使用分页查询。先是应用层使用的代码:

应用层需要构造 PagingInfo,并指定需要统计行数。查询后,直接使用 PagingInfo.TotalCount。(这种接口方案从 06 年使用至今,比较好用。)

下面是 Repository 类型上的公有接口:

最后,再实现该查询对应的数据层即可:

可以看到,在数据访问层的 ORM 框架中,主要是在 IQuery 条件类型上添加了一个 Paging 方法。使用这个方法指定了 PagingInfo 后,即按给定的分页信息分页查询实体数据了。

OEA 中的数据层分页实现

    OEA 中用到的分页有:界面层分页、DataReader 分页、数据库分页。

    目前,OEA 已经支持了 SqlServer 2005+、Oracle 10+、SqlCE4+,但是框架的设计目标则是应对所有数据库(接下来很可能需要对 MySql 进行支持)。这三种数据库中,OEA 只支持前两种大型数据库的数据库分页,主要是生成分页 SQL 进行查询。

    经过对比、挑选,我选用了一种可以在 SqlServer、Oracle 上的一种通用方案,即使用 RowNumber。例如,如果一个 SQL 查询是:

select ...... from ...... order by xxxx asc, yyyy desc

,则只需要把它转换为以下格式就行了:

select * from (select ......, row_number() over(order by xxxx asc, yyyy desc) _rowNumber from ......) x where x._rowNumber<10 and x._rowNumber>5

    同时,当需要统计总行数时,数据层会生成 SELECT COUNT(0) FROM ...... 的 SQL 语句重新进行查询,并把结果赋值给 PagingInfo.TotalCount,以及 EntityList.TotalCount。

    在 SQLCE 中,并不支持 rowNumber 函数。所以只能考虑使用 NOT IN 的 SQL 方案。其实在OEA中,鉴于实现 NOT IN 方案比较麻烦,所以决定暂时使用 DataReader 完成 SQLCE 的内存分页。

  • DataReader 内存分页

    提供 DataReader 方案主要是简单、同时还能与数据库无关,解决跨库问题。主要逻辑代码如下:

/// <summary>     
///使用 IDataReader 的内存分页读取方案。     
///      
///注意!!!     
/// 此方法中会释放 Reader。外层不能再用 Using。     
/// </summary>     
/// <param name="reader"></param>      
/// <param name="rowReader">每一行数据,会调用此方法进行调取。</param>     
/// <param name="pagingInfo">分页信息。如果这个参数不为空,则使用其中描述的分页规则进行内存分页查询。</param>     
public static void MemoryPaging(IDataReader reader, Action<IDataReader> rowReader, PagingInfo pagingInfo = null)     
{      
 bool isPaging = pagingInfo != null;     
 bool needCount = isPaging && pagingInfo.IsNeedCount;     
 int totalCount = 0;     
 int startRow = 1;//从一开始的行号     
 int endRow = int.MaxValue;     
 
 if (isPaging)     
    {      
        startRow = pagingInfo.PageSize * pagingInfo.PageIndex + 1;      
        endRow = startRow + pagingInfo.PageSize - 1;      
    }      
 
 using (reader)     
    {      
 while (reader.Read())     
        {      
            totalCount++;      
 
 if (totalCount >= startRow)     
            {      
 if (totalCount <= endRow)     
                {      
                    rowReader(reader);      
                }      
 else     
 {     
 //如果已经超出该页,而且需要统计行数,则直接快速循环到最后。     
 if (needCount)     
                    {      
 while (reader.Read()) { totalCount++; }     
 break;     
                    }      
                }      
            }      
        }      
    }      
 
 if (needCount)     
    {      
        pagingInfo.TotalCount = totalCount;      
    }      
}

通用,又简单。

待改进点

目前实现上,可能存在的缺陷是:

  • 对分页 SQL 的转换不支持复杂的嵌套 SQL。这时可能出错。

希望大伙拍砖。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏容器云生态

Docker监控方案(TIG)的研究与实践之Influxdb

前言: Influxdb也是有influxdata公司(www.influxdata.com )开发的用于数据存储的时间序列数据库.可用于数据的时间排列。在整个...

3248
来自专栏xingoo, 一个梦想做发明家的程序员

oracle数据库性能

性能视图V$开头 V$SYSTEM_EVENT 正在等待的资源的系统信息 V$SESSION_EVENT 会话累计发生的等待事件 V$SESSION_WAIT ...

2347
来自专栏杨建荣的学习笔记

MySQL高可用方案MGR+consul组合测试

今天来简单理一下MGR和consul的组合方案,前期的准备和步骤还是比较多的,晚上完成了基础的调试,来来回回切换了好多次,还算有点意思。

4793
来自专栏码神联盟

数据库 | MYSQL 中的视图view详解

序本文目录 什么是视图 视图的特性 视图的作用 视图使用场景 视图示例1-创建、查询 视图示例2-增、删、改 其它 1什么是视图 视图是一个虚拟表,其内容由查询...

45811
来自专栏杨建荣的学习笔记

关于视图和存储过程的权限问题探究 (r9笔记第87天)

今天在处理一个工单的时候发现了一个奇怪的现象,开发同学需要创建一个存储过程,目前的架构类似这样的形式 ? 数据库中存在一个属主用户,表,存储过程等对象...

35910
来自专栏漏斗社区

修仙第一步:08CMS SQL 注入

0x00 背景 本周,拿到一个源码素材是08cms的,由某师傅提供的,审计的时候发现这个CMS数据传递比较复杂,使用静态分析的方式不好操作,刚好这周小三上位(换...

39617
来自专栏云计算教程系列

如何在CentOS上安装MySQL

MySQL是一种流行的数据库管理系统,用于Web和服务器应用程序。本教程将介绍如何在CentOS 6上安装,配置和管理MySQL。

2507
来自专栏散尽浮华

Mysql备份系列(4)--lvm-snapshot备份mysql数据(全量+增量)操作记录

Mysql最常用的三种备份工具分别是mysqldump、Xtrabackup(innobackupex工具)、lvm-snapshot快照。 前面分别介绍了: ...

3799
来自专栏张善友的专栏

基于MongoDB GridFS的图片存储

它是mongodb的一个子模块,使用GridFS可以基于mongodb来持久存储文件.并且支持分布式应用(文件分布存储和读取).GridFS是mongodb中用...

56010
来自专栏ImportSource

白话数据库中的MVCC

说MVCC(Multiversion concurrency control,多版本并发控制)之前,先从数据库的ACID说起。ACID其中一个就是I。也就是Is...

4825

扫码关注云+社区

领取腾讯云代金券