自己动手写的mybatis分页插件(极其简单好用)
作者:李小拐 发布时间:2023-11-01 18:12:09
标签:mybatis,分页,插件
刚开始项目,需要用到mybatis分页,网上看了很多插件,其实实现原理基本都大同小异,但是大部分都只给了代码,注释不全,所以参考了很多篇文章(每篇文章偷一点代码,评出来自己的,半抄袭),才自己模仿着写出了一个适合自己项目的分页插件,话不多说,直接上代码,相比大部分文章,注释算很完整了
最重要的 *
package com.dnkx.interceptor;
import java.sql.*;
import java.util.HashMap;
import java.util.Properties;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
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.SystemMetaObject;
import com.dnkx.pojo.Page;
/**
*
* 分页 * ,用于拦截需要进行分页查询的操作,然后对其进行分页处理。
* 利用 * 实现Mybatis分页的原理:
* 要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句
* 是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的
* prepare方法生成的。所以利用 * 实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在 * 方法中把Sql语句改成对应的分页查询Sql语句,之后再调用
* StatementHandler对象的prepare方法,即调用invocation.proceed()。
* 对于分页而言,在 * 里面我们还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设
* 置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。
*
* 解释一下插件中可能要用到的几个类:
* MetaObject:mybatis提供的一个基于返回获取属性值的对象的类
* BoundSql : 在这个里面可以获取都要执行的sql和执行sql要用到的参数
* MappedStatement : 这个可以得到当前执行的sql语句在xml文件中配置的id的值
* RowBounds : 是mybatis内存分页要用到的。
* ParameterHandler : 是mybatis中用来替换sql中?出现的值的.
*
* @author 李小拐 2016年11月9日 10:59:04
*/
@Intercepts({
@Signature(type=StatementHandler.class,method="prepare",args={Connection.class}),
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
public class PageInterceptor implements Interceptor{
//拦截分页关键字
private static final String SELECT_ID="page";
//插件运行的代码,它将代替原有的方法,要重写最重要的intercept了
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof StatementHandler) {
//这里我们有一个设定 如果查询方法含有Page 就进行分页 其他方法无视
//所以就要获取方法名
StatementHandler statementHandler=(StatementHandler)invocation.getTarget();
MetaObject metaObject=SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement=(MappedStatement)metaObject.getValue("delegate.mappedStatement");
String selectId=mappedStatement.getId();
String methorName=selectId.substring(selectId.lastIndexOf(".")+1).toLowerCase();
//然后判断下 如果含有Page 就获取sql
if(methorName.contains(SELECT_ID)){
BoundSql boundSql=(BoundSql)metaObject.getValue("delegate.boundSql");
//分页参数作为参数对象parameterObject的一个属性
String sql=boundSql.getSql();
System.out.println("获取到的sql:"+sql);
HashMap<String, Object> map=(HashMap<String, Object>)(boundSql.getParameterObject());
//Page page=(Page)(boundSql.getParameterObject());
Page page=(Page)map.get("page");
// 重写sql
String countSql=concatCountSql(sql);
String pageSql=concatPageSql(sql,page);
// System.out.println("重写的 count sql :"+countSql);
System.out.println("重写的 select sql :"+pageSql);
Connection connection = (Connection) invocation.getArgs()[0];
PreparedStatement countStmt = null;
ResultSet rs = null;
int totalCount = 0;
try {
countStmt = connection.prepareStatement(countSql);
rs = countStmt.executeQuery();
if (rs.next()) {
totalCount = rs.getInt(1);
}
} catch (SQLException e) {
System.out.println("Ignore this exception"+e);
} finally {
try {
rs.close();
countStmt.close();
} catch (SQLException e) {
System.out.println("Ignore this exception"+ e);
}
}
metaObject.setValue("delegate.boundSql.sql", pageSql);
//绑定count
page.setNumCount(totalCount);
}
}
return invocation.proceed();
}
// 拦截类型StatementHandler,重写plugin方法
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}else {
return target;
}
}
@Override
public void setProperties(Properties properties) {
}
//改造sql
public String concatCountSql(String sql){
//StringBuffer sb=new StringBuffer("select count(*) from ");
/*sql=sql.toLowerCase();
if(sql.lastIndexOf("order")>sql.lastIndexOf(")")){
sb.append(sql.substring(sql.indexOf("from")+4, sql.lastIndexOf("order")));
}else{
sb.append(sql.substring(sql.indexOf("from")+4));
}*/
StringBuffer sb=new StringBuffer();
sql=sql.toLowerCase();
if(sql.lastIndexOf("order")>0){
sql=sql.substring(0,sql.indexOf("order"));
}
sb.append("select count(*) from ("+sql+") tmp");
return sb.toString();
}
public String concatPageSql(String sql,Page page){
StringBuffer sb=new StringBuffer();
sb.append(sql);
sb.append(" limit ").append(page.getPageBegin()).append(" , ").append(page.getPageSize());
return sb.toString();
}
}
分页对象Page类
[java] view plain copy
package com.dnkx.pojo;
import java.util.HashMap;
import java.util.Map;
/**
*
* 分页查询辅助类
* @author 李小拐 2016年11月9日 13:55:37
*/
public class Page {
//----------分页-----------
private int pageSize;//每页显示条数
private int pageCurrentPage;//第几页
private int pageBegin;//开始位置
private int numCount;//总条数
private int pageTotal;//总条数
private String orderField = "";//控制排序页面显示的
private String orderDirection = "";
public Page(){
}
public Page(int pageSize, int pageCurrentPage) {
super();
this.pageSize = pageSize;
this.pageCurrentPage = pageCurrentPage;
}
public Page(Map<String, String> map){
if(map.get("pageNum")!=null){
this.setPageCurrentPage(this.pageCurrentPage = Integer.parseInt(map.get("pageNum")));//要查询的页数
}else{
this.setPageCurrentPage(1);//设置初始值
}
if(map.get("numPerPage")!=null){
this.setPageSize(Integer.parseInt(map.get("numPerPage")));//每页显示条数
}else{
this.setPageSize(5);//设置初始值
}
if(map.get("orderField")!=null){
this.setOrderField(map.get("orderField"));
}
if(map.get("orderDirection")!=null){
this.setOrderDirection(map.get("orderDirection"));
}
}
public int getPageCurrentPage() {
return pageCurrentPage;
}
public void setPageCurrentPage(int pageCurrentPage) {
this.pageCurrentPage = pageCurrentPage;
}
public int getNumCount() {
return numCount;
}
public void setNumCount(int numCount) {
this.numCount = numCount;
}
public int getPageTotal() {
return (numCount%pageSize>0)?(numCount/pageSize+1):(numCount/pageSize);
}
public void setPageTotal(int pageTotal) {
this.pageTotal = pageTotal;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageBegin() {
return pageSize*(pageCurrentPage-1);
}
public void setPageBegin(int pageBegin) {
this.pageBegin = pageBegin;
}
public String getOrderField() {
return orderField;
}
public void setOrderField(String orderField) {
this.orderField = orderField;
}
public String getOrderDirection() {
return orderDirection;
}
public void setOrderDirection(String orderDirection) {
this.orderDirection = orderDirection;
}
public static Page getPage(int pageSize, int pageCurrentPage){
return new Page(pageSize,pageCurrentPage);
}
public static Page getPage(Map map){
return new Page(map);
}
}
Controller里面调用方式
public String list(HttpServletRequest request) {
long a=System.currentTimeMillis();
HashMap<String,Object> map=GetRequestMap.getMap(request);//自己封装的方法,取request的参数
Page page= Page.getPage(map);//初始化page
map.put("page", page);//把page对象放入参数集合(这个map是mybatis要用到的,包含查询条件,排序,分页等)
//控制排序页面显示的
map.put(map.get("orderField")+"", map.get("orderDirection"));
List<Employee> list=employeeService.getListPage(map);
request.setAttribute("emlist", list);
request.setAttribute("page", page);
request.setAttribute("map", map);
//取page相关属性
page.getNumCount();//总条数
page.getPageTotal();//总页数
long b=System.currentTimeMillis();
System.out.println("---------耗时:"+(b-a)+"ms");
return "basic/employee_list";
}
最后,spring里面配置插件
<bean id="PageInterector" class="com.dnkx.interceptor.PageInterceptor"></bean>
<!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/dnkx/mapping/*.xml"></property>
<property name="plugins">
<ref bean="PageInterector"/>
</property>
</bean>
好了,到此结束,本文仅供参考!也期待大神提意见
来源:http://blog.csdn.net/yttea/article/details/53139796


猜你喜欢
- /// <summary> /// 遍历Co
- 本篇文章讲的是自定义View之边缘凹凸的优惠券效果,之前有见过很多优惠券的效果都是使用了边缘凹凸的样式。和往常一样,主要总结一下在自定义Vi
- 1 环境部署1.1 jdk-8u111-windows-x64环境变量 JAVA_HOME:C:\Program Files\Java\jd
- session超时退到登录页面最近发现使用的工程居然没有session超时机制,功能太欠缺了,现在把追加方法分享出来,里面有一些坑,大家自由
- 一.BASIC认证概述在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,
- 推荐idea2022最新激活教程:idea2021最新激活方法https://www.jb51.net/article/197138.htm
- 做多媒体项目时,经常会最后来个客户签名并保存之类的,签名保存之前的博客Unity3d截图方法合集有介绍过了,今天闲着把断笔写字的也贴出来吧,
- 一、Nuget安装log4net --> Install-Package log4net二、在AssemblyInfo.cs文件中添加
- 刚刚接触Java不久,写法上和一些术语还不是很熟悉,如有不对的地方,希望指正。本次学生成绩系统要求能实现成绩录入, * ,绩点计算,退出系
- 1 简介Solace是一个强大的实时性的事件驱动消息队列。本文将介绍如何在Spring中使用,虽然代码使用的是Spring Boot,但并没
- Android使用GridView实现日历功能示例,代码有点多,发个图先:如果懒得往下看的,可以直接下载源码吧,最近一直有人要,由于时间太久
- 一、概述使用Java技术构建Web应用时, 我们通常离不开tomcat和jetty之类的servlet容器,这些Web服务器功能强大,性能强
- 前言在原生的 Android 或 iOS 中,都提供了基本的键值对存储方式,Android 是 SharedPreferences,iOS
- 1、数组数组其实也是一种数据格式,不过是一种复合类型,它可以存储多个同类型的值。使用数组可以将同类型的变量整合起来管理,比如说我们现在要记录
- 本文实例讲述了Android编程实现为ListView创建上下文菜单(ContextMenu)的方法。分享给大家供大家参考,具体如下:Con
- using System;using System.Collections;using System.IO;namespace Consol
- 前言今天我们继续聊聊在SprinBoot中如何集成参数校验Validator,以及参数校验的高阶技巧(自定义校验,分组校验)。&ld
- 前言 需要实现环(圆)形菜单。效果预览(更多效果请下载源码体验):实现代码1.CircularMenuItemCustomCont
- 1.生成自己的注解(为了确定在哪些位置使用)/** * 关闭patch delete的model处理,否则会报错 */@Target({El
- java 图片转base64字符串、base64字符串转图片,具体内容如下1. 图片转base64字符串: /** * base