无需代码!通过 Dataway 配置一个带有分页查询的接口

Dataway介绍

Dataway 是基于 DataQL 服务聚合能力,为应用提供的一个接口配置工具。使得使用者无需开发任何代码就配置一个满足需求的接口。 整个接口配置、测试、冒烟、发布。一站式都通过 Dataway 提供的 UI 界面完成。UI 会以 Jar 包方式提供并集成到应用中并和应用共享同一个 http 端口,应用无需单独为 Dataway 开辟新的管理端口。

这种内嵌集成方式模式的优点是,可以使得大部分老项目都可以在无侵入的情况下直接应用 Dataway。进而改进老项目的迭代效率,大大减少企业项目研发成本。

Dataway 工具化的提供 DataQL 配置能力。这种研发模式的变革使得,相当多的需求开发场景只需要配置即可完成交付。 从而避免了从数据存取到前端接口之间的一系列开发任务,例如:Mapper、BO、VO、DO、DAO、Service、Controller 统统不在需要。

大家好在《在 Spring Boot 中使用 Dataway 配置数据查询接口》文章中,我们介绍了如何使用。Dataway 来配置接口避免开发工作。这篇文章来介绍一下如何配置分页查询接口。

众所周知分页查询是绝大部分项目都无法绕过的问题,Dataway既然可以提供数据接口的配置化。那么自然也就无法绕过分页问题,好在 Dataway 在最初版本发布的时候就已经可以实现分页查询。现在我就带领大家看一看 Dataway 是怎样实现分页查询的。

DataQL分页查询文档:https://www.hasor.net/web/dataql/fragment/sql/execute.html#id5 在DataQL 中开启分页查询的官方手册在这里: 想要仔细研究的可以去这里查阅。

原理

首先讲一下分页原理,提到分页通常包含两种方式:真分页、假分页

  • 真分页:是指数据在从数据库中查询回来的数据就已经是分页之后的数据,程序无需在做过多的处理。这种一般适用于表的数据比较大的情况。
  • 假分页:是指数据会首先全部加载到程序中,然后通过程序代码返回其中一段数据。这种模式比较适用于数据不经常变化且数据量不多的场景。

在一般场景中假分页的使用越来越少,很多能力可以直接由前端组建直接实现。接下来我们就讨论一下,怎样实现真分页。

真分页,通常会考虑数据库兼容的问题。例如:下面 Myql 和 Oracle 对于数据库分页的 SQL 语句

Mysql:

select * from user_info limit m,n

Oracle:

SELECT * FROM (
  SELECT TMP_PAGE.*, ROWNUM ROW_ID FROM (
    select * from user_info
  ) TMP_PAGE)
WHERE ROW_ID > m AND ROW_ID <= n");

Dataway 也是采用这一种 SQL 改写的方式,Dataway 在实现 SQL 语句改写的时候采用数据库方言,方言的SQL语句改写上,参考了成熟的开源框架 MyBatis 分页插件 PageHelper 因此理论上可以直接兼容下列数据库。

方言版本 兼容的数据库
DB2 DB2
HsqlDB H2、HsqlDB、PostgreSQL、Phoenix
Informix Informix
Mysql Mysql、Mariadb、SQLite、HerdDB
Oracle Oracle
SqlServer2012 SqlServer2012、Derby

分页查询

首先我们延续上一篇文章中 “user_info” 表的查询例子。 首先我们知道 DataQL 默认是不开启分页的,要想实现分页需要我们自己计算 start/limit 然后通过参数的形式传给 SQL。

要想 DataQL 默认开启分页需要加入一个 Hint 将其打开:hint FRAGMENT_SQL_QUERY_BY_PAGE = true

需要在这里提醒大家的是,DataQL 的 Hint 是全局生效的,而且只能在编写 QL 查询的最前面声明。这就意味着,一旦开启了分页查询。整个 QL 查询中所有 SQL 相关的查询操作都会成为分页模式。

开启分页模式之后,通过 SQL 获取数据将会分为4个阶段:

  • 定义分页SQL
  • 创建分页查询对象
  • 设置分页信息
  • 执行分页查询

第一步 :还是定义 SQL,只不过这次我们要加上一个 hint 表示要开启分页查询。

// 步骤 1:定义分页SQL
hint FRAGMENT_SQL_QUERY_BY_PAGE = true
var dimSQL = @@sql(userName)<%
    select * from user_info where `name` like concat('%',#{userName},'%')
%>;

第二步: 获取这个 SQL 定义的分特查询对象

// 步骤 2:获取分页对象
var queryPage = dimSQL(${userName});

第三步: 设置分页信息

// 步骤 3:设置分页信息 
run pageQuery.setPageInfo({
    "pageSize"    : 5, // 页大小
    "currentPage" : 3  // 第3页
});

第四步: 执行SQL查询,获取到分页数据

下列是我们数据库中的用户数据,可以看到每页5条第三页正式我们想要的数据。( 请注意:DataQL 的分页页码是 从0 开始的

create table user_info
(
    id   int auto_increment
        primary key,
    name varchar(128) null,
    sex  int          null
);

// page 0
INSERT INTO example.user_info (id, name, sex) VALUES (1, 'user-1', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (2, 'user-2', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (3, 'user-3', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (4, 'user-4', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (5, 'user-5', 1);
// page 1
INSERT INTO example.user_info (id, name, sex) VALUES (6, 'user-6', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (7, 'user-7', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (8, 'user-8', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (9, 'user-9', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (10, 'user-10', 0);
// page 2
INSERT INTO example.user_info (id, name, sex) VALUES (11, 'user-11', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (12, 'user-12', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (13, 'user-13', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (14, 'user-14', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (15, 'user-15', 1);
// page 3
INSERT INTO example.user_info (id, name, sex) VALUES (16, 'user-16', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (17, 'user-17', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (18, 'user-18', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (19, 'user-19', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (20, 'user-21', 0);
// page 4
INSERT INTO example.user_info (id, name, sex) VALUES (21, 'user-22', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (22, 'user-23', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (23, 'user-24', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (24, 'user-25', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (25, 'user-26', 0);
// page 5
INSERT INTO example.user_info (id, name, sex) VALUES (26, 'user-27', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (27, 'user-28', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (28, 'user-29', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (29, 'user-30', 0);
INSERT INTO example.user_info (id, name, sex) VALUES (30, 'user-31', 0);
// page 6
INSERT INTO example.user_info (id, name, sex) VALUES (31, 'user-32', 1);
INSERT INTO example.user_info (id, name, sex) VALUES (32, 'user-33', 0);

分页信息

上面有了分页查询的能力,我们在前端处理分页的时候通常还会需要分页信息。有了分页信息可以生成页码按钮类似这个东西:

在DataQL 查询中可以通过下列方法拿到分页信息,这个分页信息中包含了当前分页查询的分页信息:

return queryPage.pageInfo()

// 结果

{
    "enable": true,
    "pageSize": 5,
    "totalCount": 32,
    "totalPage": 7,
    "currentPage": 3,
    "recordPosition": 15
}

组装分页接口

前面分页查询、分页信息我们都有了。剩下的就需要把 请求参数带入 DataQL 查询,并且将分页的数据组合成结果返回给前端就可以了。

首先我们把请求参数和分页参数进行代换:

接下来我们在响应结果中,把分页数据和分页信息组装到一起。

最后我们简单的用一个 Json 结构把它们返回回去。整个接口类似下面这样:

最后总结

最后我们总结一下在本文的带领下,我们通过 Dataway 内置的 SQL 执行器。实现了一个 SQL 分页查询接口的配置。 整个分页查询共计 19行代码(含注释)。

其中我们使用了分页查询的 hint FRAGMENT_SQL_QUERY_BY_PAGE

也了解了 DataQL 分页查询的步骤:

  • 定义分页SQL
  • 创建分页查询对象
  • 设置分页信息
  • 执行分页查询

最后通过 Dataway 将请求参数和响应结果封装成了一个完整的带有参数的分页查询接口,最后的最后:欢迎大家有什么问题可以在评论区留言或者加入 Hasor 官方的交流群沟通,更欢迎提 Issue 来交流讨论。

后面会有更多 Dataway 的文章推出也欢迎大家继续关注,