MyBatis實現(xiàn)物理分頁的實例
MyBatis使用RowBounds實現(xiàn)的分頁是邏輯分頁,也就是先把數據記錄全部查詢出來,然在再根據offset和limit截斷記錄返回
為了在數據庫層面上實現(xiàn)物理分頁,又不改變原來MyBatis的函數邏輯,可以編寫plugin截獲MyBatis Executor的statementhandler,重寫SQL來執(zhí)行查詢
下面的插件代碼只針對MySQL
plugin代碼
package plugin;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
/**
* 通過攔截<code>StatementHandler</code>的<code>prepare</code>方法,重寫sql語句實現(xiàn)物理分頁。
* 老規(guī)矩,簽名里要攔截的類型只能是接口。
*
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class PaginationInterceptor implements Interceptor {
private static final Log logger = LogFactory.getLog(PaginationInterceptor.class);
private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
private static String DEFAULT_PAGE_SQL_ID = ".*Page$"; // 需要攔截的ID(正則匹配)
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
DEFAULT_OBJECT_WRAPPER_FACTORY);
RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
// 分離代理對象鏈(由于目標類可能被多個攔截器攔截,從而形成多次代理,通過下面的兩次循環(huán)可以分離出最原始的的目標類)
while (metaStatementHandler.hasGetter("h")) {
Object object = metaStatementHandler.getValue("h");
metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
}
// 分離最后一個代理對象的目標類
while (metaStatementHandler.hasGetter("target")) {
Object object = metaStatementHandler.getValue("target");
metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
}
// property在mybatis settings文件內配置
Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
// 設置pageSqlId
String pageSqlId = configuration.getVariables().getProperty("pageSqlId");
if (null == pageSqlId || "".equals(pageSqlId)) {
logger.warn("Property pageSqlId is not setted,use default '.*Page$' ");
pageSqlId = DEFAULT_PAGE_SQL_ID;
}
MappedStatement mappedStatement = (MappedStatement)
metaStatementHandler.getValue("delegate.mappedStatement");
// 只重寫需要分頁的sql語句。通過MappedStatement的ID匹配,默認重寫以Page結尾的MappedStatement的sql
if (mappedStatement.getId().matches(pageSqlId)) {
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
Object parameterObject = boundSql.getParameterObject();
if (parameterObject == null) {
throw new NullPointerException("parameterObject is null!");
} else {
String sql = boundSql.getSql();
// 重寫sql
String pageSql = sql + " LIMIT " + rowBounds.getOffset() + "," + rowBounds.getLimit();
metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
// 采用物理分頁后,就不需要mybatis的內存分頁了,所以重置下面的兩個參數
metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
}
}
// 將執(zhí)行權交給下一個攔截器
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身,減少目標被代理的次數
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
} else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
配置plugin
<plugins>
<plugin interceptor="plugin.PaginationInterceptor" />
</plugins>
查詢SQL
<!-- 測試分頁查詢 -->
<select id="selectUserByPage" resultMap="dao.base.userResultMap">
<![CDATA[
SELECT * FROM user
]]>
</select>
調用示例
@Override
public List<User> selectUserByPage(int offset, int limit) {
RowBounds rowBounds = new RowBounds(offset, limit);
return getSqlSession().selectList("dao.userdao.selectUserByPage", new Object(), rowBounds);
}
另外,結合Spring MVC,編寫翻頁和生成頁碼代碼
頁碼類
package util;
/**
* Created with IntelliJ IDEA.
* User: zhenwei.liu
* Date: 13-8-7
* Time: 上午10:29
* To change this template use File | Settings | File Templates.
*/
public class Pagination {
private String url; // 頁碼url
private int pageSize = 10; // 每頁顯示記錄數
private int currentPage = 1; // 當前頁碼
private int maxPage = Integer.MAX_VALUE; // 最大頁數
// 獲取offset
public int getOffset() {
return (currentPage - 1) * pageSize;
}
// 獲取limit
public int getLimit() {
return getPageSize();
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
if (currentPage < 1)
currentPage = 1;
if (currentPage > maxPage)
currentPage = maxPage;
this.currentPage = currentPage;
}
public int getMaxPage() {
return maxPage;
}
public void setMaxPage(int maxPage) {
this.maxPage = maxPage;
}
}
為了計算最大頁碼,需要知道數據表的總記錄數,查詢SQL如下
<!-- 記錄總數 -->
<select id="countUser" resultType="Integer">
<![CDATA[
SELECT COUNT(*) FROM user
]]>
</select>
@Override
public Integer countTable() {
return getSqlSession().selectOne("dao.userdao.countUser");
}
Controller中的使用
@RequestMapping("/getUserByPage")
public String getUserByPage(@RequestParam
int page, Model model) {
pagination.setCurrentPage(page);
pagination.setUrl(getCurrentUrl());
pagination.setMaxPage(userDao.countTable() / pagination.getPageSize() + 1);
List<User> userList = userDao.selectUserByPage(
pagination.getOffset(), pagination.getLimit());
model.addAttribute(pagination);
model.addAttribute(userList);
return "index";
}
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
關于JAVA_HOME路徑修改之后JDK的版本依然不更改的解決辦法
今天小編就為大家分享一篇關于JAVA_HOME路徑修改之后JDK的版本依然不更改的解決辦法,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04
SpringBoot使用itext填充pdf表單及導出pdf的流程
由于最近開發(fā)的項目需要用到打印單據,就在網上找了一下方案,反反復復,都沒有找到合適的,借鑒了網上資源,使用itext5、itext7的工具包,所以本文介紹了SpringBoot使用itext填充pdf表單及導出pdf的流程,需要的朋友可以參考下2024-09-09
Mybatis-Plus設置全局或者局部ID自增的實現(xiàn)
在使用Mybatis-Plus新增的時候,我們往往想要id隨著數據庫自增,本文主要介紹了Mybatis-Plus設置全局或者局部ID自增的實現(xiàn),具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-01-01
SpringBoot使用AOP實現(xiàn)日志記錄功能詳解
這篇文章主要為大家介紹了SpringBoot使用AOP實現(xiàn)日志記錄功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07
java實現(xiàn)String類型和Date類型相互轉換
很多人表示,java將string類型轉為date類型不知道應該怎樣做,本文就來介紹一下java實現(xiàn)String類型和Date類型相互轉換,具有一定的參考價值,感興趣的可以了解一下2023-10-10

