跳到主要内容

分页与方言

分页查询

dbVisitor 内置了分页查询机制,使用方便且无需任何配置。具体工作方式为:

  • 框架会先生成一条未经改写的最终执行 SQL 及参数,并通过 BoundSql 类型承载。
  • 然后会根据 PageSqlDialect 接口将原始 SQL 处理成分页 SQL。结果仍然是 BoundSql 类型承载。
  • 执行最终的 SQL 完成分页查询

LambdaTemplate 方式

使用 LambdaTemplate 进行分页查询

// 构造 LambdaTemplate 和初始化一些数据
DataSource dataSource = DsUtils.dsMySql();
LambdaTemplate lambdaTemplate = new LambdaTemplate(dataSource);
lambdaTemplate.loadSQL("CreateDB.sql");

// 构建分页对象,每页 3 条数据(默认第一页的页码为 0)
Page pageInfo = new PageObject();
pageInfo.setPageSize(3);

// 分页查询数据
List<TestUser> pageData1 = lambdaTemplate.lambdaQuery(TestUser.class)
.usePage(pageInfo)
.queryForList();

// 分页查询下一页数据
pageInfo.nextPage();
List<TestUser> pageData2 = lambdaTemplate.lambdaQuery(TestUser.class)
.usePage(pageInfo)
.queryForList();

DalSession 方式

以一个简单SQL语句为例,演示 mapper 层面支持分页查询:

<select id="queryListByAge">
select * from `test_user` where age = #{age}
</select>

Mapper 无需任何修改,若使用 DalSession 方式执行分页查询。需要构建分页对象然后传递给 queryStatement 方法。

Map<String, Object> ages = new HashMap<>();
ages.put("age", 26);

PageObject page = new PageObject(); // 分页查询对象
page.setPageSize(20);
List<Object> result = dalSession.queryStatement("queryListByAge", ages, page);

Mapper 接口方式

首先将 Mapper queryListByAge 方法映射到接口上,然后在接口方法中增加 Page 分页查询参数对象。例如:

@RefMapper("...")
public interface PageExecuteDal {
List<TestUser> queryListByAge(@Param("age") int age, Page pageInfo);
}

或者如下。区别于上面的是可以选择返回一个分页查询结果 PageResult,分页结果对象中包含了分页查询信息和总数数据。

提示

使用分页查询结果对象会产生额外的一条 count 查询。

@RefMapper("...")
public interface PageExecuteDal {
PageResult<TestUser> queryListByAge(@Param("name") String name, Page pageInfo);
}

分页对象

dbVisitor 提供了一个分页查询工具类 PageObject, 它实现了 Page 接口。并提供了如下一些工具属性/方法。

名称描述
属性 pageSize分页查询的页大小,默认是 -1 表示无穷大
属性 currentPage当前页号
属性 pageNumberOffset页码偏移量(例如:从 1 页作为起始页,可以设置为 1。否则第一页的页码是 0
方法 long getFirstRecordPosition()获取本页第一个记录的索引位置
方法 long getTotalPage()获取总页数
方法 long getTotalCount()获取记录总数
方法 long firstPage()移动到第一页
方法 void previousPage()移动到上一页
方法 void nextPage()移动到下一页
方法 void lastPage()移动到最后一页
方法 Map<String, Object> toPageInfo()获取分页信息

toPageInfo 方法会返回如下一个分页查询 Map。

{
"enable" : true, // 是否启用分页
"pageSize" : 20, // 页大小
"totalCount" : 200, // 总记录数
"totalPage" : 10, // 页总数
"currentPage" : 0, // 当前页码
"recordPosition" : 0// 第一条记录的起始记录位置
}

方言

和方言相关的接口一共有 4 个,其中 SqlDialect 是所有其它接口都继承的公共接口。

  • SqlDialect 基础接口,负责管理关键词清单、生成表名、列名
  • ConditionSqlDialect 负责条件相关的生成,例如:like 语句
  • InsertSqlDialect 负责高级 insert 语句生成,例如处理:冲突策略
  • PageSqlDialect 负责分页语句生成。
提示

实现自定义方言最佳的路线是继承 AbstractDialect 抽象类。

内置分页查询方言实现

dbVisitor 默认会根据 JDBC 的链接字符串自动匹配方言,支持如下数据库:

数据库编码方言类名JDBC串识别前缀
DB2db2Db2Dialectjdbc:db2:***
Apache DerbyderbyDerbyDialectjdbc:derby:***jdbc:log4jdbc:derby:***
达梦dmDmDialectjdbc:dm:***
H2H2H2Dialectjdbc:h2::***jdbc:log4jdbc:h2:***
HivehiveHiveDialectjdbc:hive:***jdbc:hive2:***
HSQLhsqlHSQLDialectjdbc:hsqldb:***jdbc:log4jdbc:hsqldb:***
Apache ImpalaimpalaImpalaDialectjdbc:impala:***
IBM InformixinformixInformixDialectjdbc:informix-sqli:***jdbc:log4jdbc:informix-sqli:***
人大金仓kingbaseKingbaseDialectjdbc:kingbase:***
MariaDBmariadbMariaDBDialectjdbc:mariadb:***
MYSQLmysqlMySqlDialectjdbc:mysql:***jdbc:cobar:***jdbc:log4jdbc:mysql:***
OracleoracleOracleDialectjdbc:oracle:***jdbc:log4jdbc:oracle:***
PhoenixphoenixPhoenixDialectjdbc:phoenix:***
PostgreSQLpostgresqlPostgreSqlDialectjdbc:postgresql:***jdbc:log4jdbc:postgresql:***
SQLitesqliteSqlLiteDialectjdbc:sqlite:***
SQL ServersqlserverSqlServerDialectjdbc:jtds:***jdbc:microsoft:***jdbc:sqlserver:***jdbc:log4jdbc:jtds:***jdbc:log4jdbc:microsoft:***jdbc:log4jdbc:sqlserver:***
虚谷数据库xuguXuGuDialectjdbc:xugu:***

自定义方言

若想实现 分页查询方言 自定义只需要继承 AbstractDialect 抽象类,然后额外在实现 PageSqlDialect 接口即可。

PageSqlDialect 接口有两个方法,分别用于生成改写后的分页查询 SQL、以及计算 count 的 SQL。

public interface PageSqlDialect extends SqlDialect {
default BoundSql countSql(BoundSql boundSql) {
return new BoundSql.BoundSqlObj("SELECT COUNT(*) FROM (" + boundSql.getSqlString() + ") as TEMP_T", boundSql.getArgs());
}

BoundSql pageSql(BoundSql boundSql, int start, int limit);
}
提示

若计算总数的 SQL 只是简单的将其放入子查询并且 count 一下,那么只实现 pageSql 用于改写分页语句的 SQL 即可。

使用自己的新分页查询方言

对于 LambdaTemplate 类通过下面方式来设置自己的方言实现类

LambdaTemplate lambdaTemplate = ...
lambdaTemplate.setDialect(new MyDialect());

对于 DalSession 需要通过构造方法穿传入。

MappingOptions opt = MappingOptions.buildNew().defaultDialect(new MyDialect());
DalRegistry dalRegistry = new DalRegistry(opt);
DataSource dataSource = ...
DalSession dalSession = new DalSession(dataSource, dalRegistry);

通过注册方式,然后 dbVisitor 自动使用新分页查询方言。

以 MySQL 为例,MySQL 的链接字符串格式为 jdbc:mysql:***

  • 首先 dbVisitor 会通过 connection.getMetaData().getURL() 方式拿到链接字符串
  • 然后根据上面 内置方言 表格中的信息匹配到对应的 方言编码
  • 最后通过 SqlDialectRegister.findOrCreate(<方言编码>); 方法获取到对应的方言对象。

因此只需要按照上面规则将新的方言注册到 SqlDialectRegister 上即可。如下:

SqlDialectRegister.registerDialectAlias(JdbcUtils.MYSQL, MyDialect.class);