1. Плагин пейджингаPagehelper
PageHelper
даMybatis
Плагин пейджинга, очень простой в использовании!
1.1 Spring Boot
полагаться
<!-- pagehelper 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
Его также можно импортировать
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>latest version</version>
</dependency>
1.2 PageHelper
настроить
добавлен файл конфигурацииPageHelper
Конфигурация в основном задает диалект пейджинга и поддерживает параметры интерфейса для передачи параметров пейджинга следующим образом:
pagehelper:
# 指定数据库
helper-dialect: mysql
# 默认是false。启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages(最大页数)会查询最后一页。禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据
reasonable: false
# 是否支持接口参数来传递分页参数,默认false
support-methods-arguments: true
# 为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值, 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
params: count=countSql
row-bounds-with-count: true
Полный конфигурационный файл проекта подробно описан в текстеmybatis-pagehelper.
1.3 Как разбить на страницы
просто следуйтеPageHelper.startPage
первый после методаMybatis
запрос (Select
) будет автоматически разбивать на страницы!!!!
@Test
public void selectForPage() {
// 第几页
int currentPage = 2;
// 每页数量
int pageSize = 5;
// 排序
String orderBy = "id desc";
PageHelper.startPage(currentPage, pageSize, orderBy);
List<UserInfoPagehelperDO> users = userInfoPagehelperMapper.selectList();
PageInfo<UserInfoPagehelperDO> userPageInfo = new PageInfo<>(users);
log.info("userPageInfo:{}", userPageInfo);
}
...: userPageInfo:PageInfo{pageNum=2, pageSize=5, size=1, startRow=6, endRow=6, total=6, pages=2, list=Page{count=true, pageNum=2, pageSize=5, startRow=5, endRow=10, total=6, pages=2, reasonable=false, pageSizeZero=false}[UserInfoPagehelperDO{id=1, userName='null', age=22, createTime=null}], prePage=1, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=8, navigateFirstPage=1, navigateLastPage=2, navigatepageNums=[1, 2]}
Возвращаемые результаты включают в себя данные, будь то первая/последняя страница, общее количество страниц и общее количество записей.Mybatis-PageHelper
Полный пример разбиения на страницы Pagehelper
два,Mybatis
Перехватчик реализует пейджинг
2.1 Mybatis
перехватчик
Официальный сайт MybatisРаздел [Плагины] имеет следующее описание:
- пройти через
MyBatis
Мощный механизм, предусмотренный, используя плагины очень просты, просто реализуютInterceptor
интерфейс и укажите сигнатуру метода, который вы хотите перехватить. -
MyBatis
Позволяет перехватывать вызовы в момент выполнения сопоставленного оператора. по умолчанию,MyBatis
Вызовы методов, которые разрешено перехватывать с помощью плагинов, включают:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
То есть: мы можем добиться этого с помощью перехватчиковMyBatis
Плагин (разбивка данных на страницы)
Далее я сосредоточусь на демонстрации того, как я использовал перехватчики для реализации пейджинга.
2.2 Форма вызова
Прежде чем смотреть, как это реализовать, давайте посмотрим, как использовать:
копироватьPageHelper
дизайн, сначала вызовите статический метод, для первого метода нижеsql
заявление о перехвате, вnew
Автоматически обрабатывать при подкачке объекта.
@Test
public void selectForPage() {
// 该查询进行分页,指定第几页和每页数量
PageInterceptor.startPage(1,2);
List<UserInfoDO> all = dao.findAll();
PageResult<UserInfoDO> result = new PageResult<>(all);
// 分页结果打印
System.out.println("总记录数:" + result.getTotal());
System.out.println(result.getData().toString());
}
Затем мы в основном рассмотрим этапы реализации.
2.3 Диалекты базы данных
Определите интерфейс диалекта и используйте разные диалекты для реализации разных данных.
Dialect.java
public interface Dialect {
/**
* 获取count SQL语句
*
* @param targetSql
* @return
*/
default String getCountSql(String targetSql) {
return String.format("select count(1) from (%s) tmp_count", targetSql);
}
/**
* 获取limit SQL语句
* @param targetSql
* @param offset
* @param limit
* @return
*/
String getLimitSql(String targetSql, int offset, int limit);
}
-
Mysql
Диалект пагинации
@Component
public class MysqlDialect implements Dialect{
private static final String PATTERN = "%s limit %s, %s";
private static final String PATTERN_FIRST = "%s limit %s";
@Override
public String getLimitSql(String targetSql, int offset, int limit) {
if (offset == 0) {
return String.format(PATTERN_FIRST, targetSql, limit);
}
return String.format(PATTERN, targetSql, offset, limit);
}
}
2.4 Основная логика перехватчика
Полный код этой части см.PageInterceptor.java
- Внутренний класс вспомогательного параметра пейджинга
PageParam.java
public static class PageParam {
// 当前页
int pageNum;
// 分页开始位置
int offset;
// 分页数量
int limit;
// 总数
public int totalSize;
// 总页数
public int totalPage;
}
- Запросить общее количество записей
private long queryTotal(MappedStatement mappedStatement, BoundSql boundSql) throws SQLException {
Connection connection = null;
PreparedStatement countStmt = null;
ResultSet rs = null;
try {
connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
String countSql = this.dialect.getCountSql(boundSql.getSql());
countStmt = connection.prepareStatement(countSql);
BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
setParameters(countStmt, mappedStatement, countBoundSql, boundSql.getParameterObject());
rs = countStmt.executeQuery();
long totalCount = 0;
if (rs.next()) {
totalCount = rs.getLong(1);
}
return totalCount;
} catch (SQLException e) {
log.error("查询总记录数出错", e);
throw e;
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
log.error("exception happens when doing: ResultSet.close()", e);
}
}
if (countStmt != null) {
try {
countStmt.close();
} catch (SQLException e) {
log.error("exception happens when doing: PreparedStatement.close()", e);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
log.error("exception happens when doing: Connection.close()", e);
}
}
}
}
- Пагинация
SQL
параметр?
установить значение
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
Object parameterObject) throws SQLException {
ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
parameterHandler.setParameters(ps);
}
- Заменить оригинальный диалектным интерфейсом
SQL
утверждение
private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
StringBuffer keyProperties = new StringBuffer();
for (String keyProperty : ms.getKeyProperties()) {
keyProperties.append(keyProperty).append(",");
}
keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
builder.keyProperty(keyProperties.toString());
}
//setStatementTimeout()
builder.timeout(ms.getTimeout());
//setStatementResultMap()
builder.parameterMap(ms.getParameterMap());
//setStatementResultMap()
builder.resultMaps(ms.getResultMaps());
builder.resultSetType(ms.getResultSetType());
//setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
- Подсчитать общее количество страниц
public int countPage(int totalSize, int offset) {
int totalPageTemp = totalSize / offset;
int plus = (totalSize % offset) == 0 ? 0 : 1;
totalPageTemp = totalPageTemp + plus;
if (totalPageTemp <= 0) {
totalPageTemp = 1;
}
return totalPageTemp;
}
- статический метод пагинации для вызова
Я разработал его здесь, количество страниц от
1
в начале, если вы привыкли0
Для начала вы можете изменить его самостоятельно.
public static void startPage(int pageNum, int pageSize) {
int offset = (pageNum-1) * pageSize;
int limit = pageSize;
PageInterceptor.PageParam pageParam = new PageInterceptor.PageParam();
pageParam.offset = offset;
pageParam.limit = limit;
pageParam.pageNum = pageNum;
PARAM_THREAD_LOCAL.set(pageParam);
}
2.5 Набор результатов пейджинга
Чтобы облегчить инкапсуляцию результатов, я инкапсулировал здесь относительно полный набор результатов разбиения по страницам, который содержит слишком много вещей.Давайте медленно рассмотрим следующие свойства (я думаю, что они более полны, добро пожаловать в лицо)
public class PageResult<T> implements Serializable {
/**
* 是否为第一页
*/
private Boolean isFirstPage = false;
/**
* 是否为最后一页
*/
private Boolean isLastPage = false;
/**
* 当前页
*/
private Integer pageNum;
/**
* 每页的数量
*/
private Integer pageSize;
/**
* 总记录数
*/
private Integer totalSize;
/**
* 总页数
*/
private Integer totalPage;
/**
* 结果集
*/
private List<T> data;
public PageResult() {
}
public PageResult(List<T> data) {
this.data = data;
PageInterceptor.PageParam pageParam = PageInterceptor.PARAM_THREAD_LOCAL.get();
if (pageParam != null) {
pageNum = pageParam.pageNum;
pageSize = pageParam.limit;
totalSize = pageParam.totalSize;
totalPage = pageParam.totalPage;
isFirstPage = (pageNum == 1);
isLastPage = (pageNum == totalPage);
PageInterceptor.PARAM_THREAD_LOCAL.remove();
}
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotalSize() {
return totalSize;
}
public void setTotalSize(Integer totalSize) {
this.totalSize = totalSize;
}
public Integer getTotalPage() {
return totalPage;
}
public void setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public Boolean getFirstPage() {
return isFirstPage;
}
public void setFirstPage(Boolean firstPage) {
isFirstPage = firstPage;
}
public Boolean getLastPage() {
return isLastPage;
}
public void setLastPage(Boolean lastPage) {
isLastPage = lastPage;
}
@Override
public String toString() {
return "PageResult{" +
"isFirstPage=" + isFirstPage +
", isLastPage=" + isLastPage +
", pageNum=" + pageNum +
", pageSize=" + pageSize +
", totalSize=" + totalSize +
", totalPage=" + totalPage +
", data=" + data +
'}';
}
}
2.6 При простом тесте
@Test
public void selectForPage() {
// 该查询进行分页,指定第几页和每页数量
PageInterceptor.startPage(1,4);
List<UserInfoDO> all = userMapper.findAll();
PageResult<UserInfoDO> result = new PageResult<>(all);
// 分页结果打印
log.info("总记录数:{}", result.getTotalSize());
log.info("list:{}", result.getData());
log.info("result:{}", result);
}
Основное использование
1.3
Точно то же самое, только инкапсулированное в мой собственный набор результатов с разбивкой на страницы.
- Журнал выглядит следующим образом:
....: ==> Preparing: SELECT id, user_name, age, create_time FROM user_info_pageable limit 4
....: ==> Parameters:
....: <== Total: 4
....: 总记录数:6
....: list:[UserInfoDO(id=1, userName=张三, age=22, createTime=2019-10-08T20:52:46), UserInfoDO(id=2, userName=李四, age=21, createTime=2019-12-23T20:22:54), UserInfoDO(id=3, userName=王二, age=22, createTime=2019-12-23T20:23:15), UserInfoDO(id=4, userName=马五, age=20, createTime=2019-12-23T20:23:15)]
....: result:PageResult{isFirstPage=true, isLastPage=false, pageNum=1, pageSize=4, totalSize=6, totalPage=2, data=[UserInfoDO(id=1, userName=张三, age=22, createTime=2019-10-08T20:52:46), UserInfoDO(id=2, userName=李四, age=21, createTime=2019-12-23T20:22:54), UserInfoDO(id=3, userName=王二, age=22, createTime=2019-12-23T20:23:15), UserInfoDO(id=4, userName=马五, age=20, createTime=2019-12-23T20:23:15)]}
При анализе журналов установлено, что общиеSELECT * FROM user_info_pageable
собран вSELECT * FROM user_info_pageable limit 4
, указывающий, что пейджинг, реализованный перехватчиком, выполнен успешно.
3. Резюме
Два пути:Pagehelper
Пейджинг и самостоятельная реализация, выберите его в соответствии с реальной ситуацией.
3.1 Ежедневная похвала
- читы семейной реликвииВесенняя коллекция подсолнуховОткрытый исходный код, добро пожаловать, чтобы жаловаться и давать подсказки!
- Магия Цзюян【Записная книжка по Java】Открытый исходный код, добро пожаловать, чтобы жаловаться и давать подсказки!
3.2 Культурный обмен
Последняя статья, добро пожаловать на внимание: публичный аккаунт-блог fengchen; обмен мнениями, добро пожаловать на добавление: личный WeChat