原理:通过拦截器,在mybatis执行sql语句前,对sql语句进行处理,最后获得所需的所有分页数据。
步骤:
1.需要创建四个类:PageData,PageNav,PageRequest,PageInterceptor,ReflectHelper
2.其作用:
PageData:用于包含分页的所有信息
PageNav:分页导航信息
PageRequest:分页请求对象,用于进行分页时传入的参数
PageInterceptor:分页用到的拦截器
ReflectHelper:用于反射执行的对象或方法。
3.用法:
1)配置拦截器:
<configuration>
<plugins>
<plugin interceptor="拦截器包路径">
<property name="dialect" value="mysql(指对应的数据库类型)" />
<property name="pageSqlId" value=".*page(拦截器拦截的规则,要求执行的sql方法要以page结尾)" />
</plugin>
</plugins>
</configuration>
2)mybatis的sql类型中的select元素中id的值要以page结尾
<select id="hahapage" resultMap="BaseResultMap">
select * from user
</select>
3.代码如下:
PageData代码:
public class PageData<T> {
protected final int pageSize;
protected final int firstIndex;
protected final int lastIndex;
protected final List<T> items;
protected final int totalItems;
protected final int totalPages;
protected int currentPage;
protected final int pageItemsCount;
public int getPageSize() {
return this.pageSize;
}
public int getFirstIndex() {
return this.firstIndex;
}
public int getLastIndex() {
return this.lastIndex;
}
public List<T> getItems() {
return this.items;
}
public int getTotalItems() {
return this.totalItems;
}
public int getTotalPages() {
return this.totalPages;
}
public int getCurrentPage() {
return this.currentPage;
}
public int getPageItemsCount() {
return this.pageItemsCount;
}
public PageData() {
this(1, 0, PageRequest.defaultPageSize, (List) null);
}
public PageData(int page, int size) {
this(page, size, PageRequest.defaultPageSize, (List) null);
}
public PageData(int page, int size, int pageSize) {
this(page, size, pageSize, (List) null);
}
public PageData(PageRequest req, int size, List<T> items) {
this(req.getPage(), size, req.getSize(), items);
}
public PageData(int page, int size, int pageSize, List<T> items) {
if (pageSize <= 0) {
pageSize = PageRequest.defaultPageSize;
}
this.pageSize = pageSize;
this.totalItems = size;
this.items = items;
this.totalPages = this.totalItems % pageSize == 0 ? this.totalItems / pageSize : this.totalItems / pageSize + 1;
if (page < 1) {
page = 1;
}
if (page > this.totalPages) {
page = this.totalPages;
}
this.currentPage = page;
this.firstIndex = calcFirstItemIndexOfPage(page, pageSize, size);
int last = this.isLastPage() ? this.totalItems - 1 : this.firstIndex + pageSize - 1;
int itemsPerPage = last - this.firstIndex + 1;
if (last < 0) {
last = 0;
itemsPerPage = 0;
}
this.lastIndex = last;
this.pageItemsCount = itemsPerPage;
}
public boolean hasNextPage() {
return this.currentPage < this.totalPages - 1;
}
public boolean isLastPage() {
return this.currentPage == this.totalPages;
}
public boolean hasPreviousPage() {
return this.currentPage > 1;
}
public boolean isFirstPage() {
return this.currentPage == 1 || this.currentPage == 0;
}
public String toString() {
return "" + '[' + this.currentPage + ',' + this.totalPages + ',' + this.totalItems + ',' + this.firstIndex + ','
+ this.lastIndex + ',' + this.pageSize + ']';
}
public static int calcPageOfItem(int itemIndex, int pageSize) {
return itemIndex / pageSize + 1;
}
public static int calcFirstItemIndexOfPage(int page, int pageSize, int total) {
if (total == 0) {
return 0;
} else {
if (page < 1) {
page = 1;
}
int first = (page - 1) * pageSize;
if (first >= total) {
first = (total - 1) / pageSize * pageSize;
}
return first;
}
}
public static int calcFirstItemIndexOfPage(PageRequest pageRequest, int total) {
return calcFirstItemIndexOfPage(pageRequest.getPage(), pageRequest.getSize(), total);
}
}
PageNav 的代码:
public class PageNav {
public static final int DEFAULT_SHOWN = 10;
protected int from;
protected int to;
public PageNav(PageData<?> pageData) {
this(pageData.getTotalPages(), pageData.getCurrentPage(), 10);
}
public PageNav(PageData<?> pageData, int shown) {
this(pageData.getTotalPages(), pageData.getCurrentPage(), shown);
}
public PageNav(int total, int current, int shown) {
if (total != 0) {
if (total <= shown) {
this.from = 1;
this.to = total;
} else {
int leftMin = shown / 2;
int rightMin = leftMin - 1;
this.from = current - leftMin;
if (this.from < 1) {
this.from = 1;
this.to = shown;
} else {
this.to = current + rightMin;
if (this.to > total) {
this.to = total;
this.from = this.to - shown + 1;
}
}
}
}
}
public int getFrom() {
return this.from;
}
public int getTo() {
return this.to;
}
}
PageRequest的代码:
public class PageRequest {
public static int defaultPageSize = 10;
public static int defaultSortIndex = 0;
protected int page = 1;
protected int size;
protected String pagerId;
protected int sort;
protected int totalItems;
public PageRequest() {
this.size = defaultPageSize;
this.sort = defaultSortIndex;
}
public int getPage() {
return this.page;
}
public void setPage(int page) {
this.page = page > 0 ? page : 1;
}
public int getSize() {
return this.size;
}
public void setSize(int size) {
this.size = size > 0 ? size : defaultPageSize;
}
public int getSort() {
return this.sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public String getPagerId() {
return this.pagerId;
}
public void setPagerId(String pagerId) {
this.pagerId = pagerId;
}
public int calcOffset() {
return (this.page - 1) * this.size;
}
public String toString() {
return "PageRequest{page=" + this.page + ", size=" + this.size + ", sort=" + this.sort + ", pagerId="
+ this.pagerId + "}";
}
public int getTotalItems() {
return this.totalItems;
}
}
PageInterceptor的代码:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
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.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {
private String dialect;
private String pageSqlId;
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof RoutingStatementHandler) {
RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation.getTarget();
BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler,
"delegate");
MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate,
"mappedStatement");
if (mappedStatement.getId().matches(this.pageSqlId)) {
BoundSql boundSql = delegate.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
if (parameterObject == null) {
throw new NullPointerException("parameterObject error");
}
Connection connection = (Connection) invocation.getArgs()[0];
String sql = boundSql.getSql();
String countSql = "select count(0) from (" + sql + ") myCount";
PreparedStatement countStmt = connection.prepareStatement(countSql);
BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
boundSql.getParameterMappings(), parameterObject);
this.setParameters(countStmt, mappedStatement, countBS, parameterObject);
ResultSet rs = countStmt.executeQuery();
int count = 0;
if (rs.next()) {
count = rs.getInt(1);
}
rs.close();
countStmt.close();
PageRequest page = null;
if (parameterObject instanceof PageRequest) {
page = (PageRequest) parameterObject;
} else if (parameterObject instanceof Map) {
Map pageSql = (Map) parameterObject;
page = (PageRequest) pageSql.get("page");
} else {
page = (PageRequest) ReflectHelper.getValueByFieldName(parameterObject, "page");
}
ReflectHelper.setValueByFieldName(page, "totalItems", Integer.valueOf(count));
String pageSql1 = this.generatePageSql(sql, page);
ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql1);
}
}
return invocation.proceed();
}
private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
Object parameterObject) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
Configuration configuration = mappedStatement.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
for (int i = 0; i < parameterMappings.size(); ++i) {
ParameterMapping parameterMapping = (ParameterMapping) parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();
PropertyTokenizer prop = new PropertyTokenizer(propertyName);
Object value;
if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (propertyName.startsWith("__frch_") && boundSql.hasAdditionalParameter(prop.getName())) {
value = boundSql.getAdditionalParameter(prop.getName());
if (value != null) {
value = configuration.newMetaObject(value)
.getValue(propertyName.substring(prop.getName().length()));
}
} else {
value = metaObject == null ? null : metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
if (typeHandler == null) {
throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName
+ " of statement " + mappedStatement.getId());
}
typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
}
}
}
}
private String generatePageSql(String sql, PageRequest page) {
if (page != null && (this.dialect != null || !this.dialect.equals(""))) {
StringBuffer pageSql = new StringBuffer();
if ("mysql".equals(this.dialect)) {
pageSql.append(sql);
int arg10000 = page.calcOffset();
pageSql.append(" limit " + arg10000 + "," + page.getSize());
} else if ("oracle".equals(this.dialect)) {
pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
pageSql.append(sql);
pageSql.append(") tmp_tb where ROWNUM<=");
pageSql.append(page.calcOffset() + page.getSize());
pageSql.append(") where row_id>");
pageSql.append(page.calcOffset());
}
return pageSql.toString();
} else {
return sql;
}
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
this.dialect = properties.getProperty("dialect");
if (this.dialect == null || this.dialect.equals("")) {
try {
throw new RuntimeException("dialect property is not found!");
} catch (RuntimeException arg3) {
arg3.printStackTrace();
}
}
this.pageSqlId = properties.getProperty("pageSqlId");
if (this.dialect == null || this.dialect.equals("")) {
try {
throw new RuntimeException("pageSqlId property is not found!");
} catch (RuntimeException arg2) {
arg2.printStackTrace();
}
}
}
}
ReflectHelper代码:
import java.lang.reflect.Field;
public class ReflectHelper {
public static Field getFieldByFieldName(Object obj, String fieldName) {
Class superClass = obj.getClass();
while (superClass != Object.class) {
try {
return superClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException arg3) {
superClass = superClass.getSuperclass();
}
}
return null;
}
public static Object getValueByFieldName(Object obj, String fieldName)
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field field = getFieldByFieldName(obj, fieldName);
Object value = null;
if (field != null) {
if (field.isAccessible()) {
value = field.get(obj);
} else {
field.setAccessible(true);
value = field.get(obj);
field.setAccessible(false);
}
}
return value;
}
public static void setValueByFieldName(Object obj, String fieldName, Object value)
throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(fieldName);
if (field.isAccessible()) {
field.set(obj, value);
} else {
field.setAccessible(true);
field.set(obj, value);
field.setAccessible(false);
}
}
}
该博客介绍了一种通过MyBatis拦截器实现逻辑分页的方法。首先创建了PageData、PageNav、PageRequest和PageInterceptor四个类,分别用于存储分页信息、分页导航、分页请求和拦截SQL进行分页处理。PageInterceptor拦截以'page'结尾的SQL,计算总页数和当前页数据。然后通过配置拦截器并修改SQL的id为以page结尾,实现自动分页功能。
1993

被折叠的 条评论
为什么被折叠?



