当前位置: 移动技术网 > IT编程>开发语言>Java > Mybatis拦截器实现分页

Mybatis拦截器实现分页

2019年07月22日  | 移动技术网IT编程  | 我要评论

最终dao层结果:

public interface modelmapper {
 page<model> pagebyconditions(rowbounds rowbounds, model record); 
}

接下来一步一步来实现分页。

一.创建page对象:

public class page<t> extends pagelist<t> {
 private int pageno = 1;// 页码,默认是第一页
 private int pagesize = 15;// 每页显示的记录数,默认是15
 private int totalrecord;// 总记录数
 private int totalpage;// 总页数
 public page() {
 }
 public page(int pageno, int pagesize, int totalrecord,
  list<t> results) {
 this.pageno = pageno;
 this.pagesize = pagesize;
 this.totalrecord = totalrecord;
 this.setresult(results);
 int totalpage = totalrecord % pagesize == 0 ? totalrecord / pagesize : totalrecord / pagesize + 1;
 this.settotalpage(totalpage);
 }
 public int getpageno() {
 return pageno;
 }
 public void setpageno(int pageno) {
 this.pageno = pageno;
 }
 public int getpagesize() {
 return pagesize;
 }
 public void setpagesize(int pagesize) {
 this.pagesize = pagesize;
 }
 public int gettotalrecord() {
 return totalrecord;
 }
 public void settotalrecord(int totalrecord) {
 this.totalrecord = totalrecord;
 // 在设置总页数的时候计算出对应的总页数,在下面的三目运算中加法拥有更高的优先级,所以最后可以不加括号。
 int totalpage = totalrecord % pagesize == 0 ? totalrecord / pagesize : totalrecord / pagesize + 1;
 this.settotalpage(totalpage);
 }
 public int gettotalpage() {
 return totalpage;
 }
 public void settotalpage(int totalpage) {
 this.totalpage = totalpage;
 }
 @override
 public string tostring() {
 stringbuilder builder = new stringbuilder();
 builder.append("page [pageno=").append(pageno).append(", pagesize=").append(pagesize).append(", results=")
  .append(getresult()).append(", totalpage=").append(totalpage).append(", totalrecord=").append(totalrecord)
  .append("]");
 return builder.tostring();
 }
}

可以发现,这里继承了一个pagelist类;这个类也是自己创建的一个类,实现list接口。为什么要pagelist这个类,是因为page需要实现list接口,而接口中的抽象方法,需要逐一实现,所以提供pagelist在统一的地方写实现list接口的方法。

为什么page需要实现list接口,这个会在稍后的代码中做解释。

pagelist类:

public class pagelist<t> implements list<t> {
 private list<t> result;
 public list<t> getresult() {
 return result;
 }
 public void setresult(list<t> result) {
 this.result = result;
 }
 @override
 public int size() {
 return result.size();
 }
 @override
 public boolean isempty() {
 return result.isempty();
 }
 @override
 public boolean contains(object o) {
 return result.contains(o);
 }
 @override
 public iterator<t> iterator() {
 return result.iterator();
 }
 @override
 public object[] toarray() {
 return result.toarray();
 }
 @override
 public <e> e[] toarray(e[] a) {
 return result.toarray(a);
 }
 @override
 public boolean add(t e) {
 return result.add(e);
 }
 @override
 public boolean remove(object o) {
 return result.remove(o);
 }
 @override
 public boolean containsall(collection<?> c) {
 return result.containsall(c);
 }
 @override
 public boolean addall(collection<? extends t> c) {
 return result.addall(c);
 }
 @override
 public boolean addall(int index, collection<? extends t> c) {
 return result.addall(index, c);
 }
 @override
 public boolean removeall(collection<?> c) {
 return result.removeall(c);
 }
 @override
 public boolean retainall(collection<?> c) {
 return result.retainall(c);
 }
 @override
 public void clear() {
 result.clear();
 }
 @override
 public t get(int index) {
 return result.get(index);
 }
 @override
 public t set(int index, t element) {
 return result.set(index, element);
 }
 @override
 public void add(int index, t element) {
 result.add(index, element);
 }
 @override
 public t remove(int index) {
 return result.remove(index);
 }
 @override
 public int indexof(object o) {
 return result.indexof(o);
 }
 @override
 public int lastindexof(object o) {
 return result.lastindexof(o);
 }
 @override
 public listiterator<t> listiterator() {
 return result.listiterator();
 }
 @override
 public listiterator<t> listiterator(int index) {
 return result.listiterator(index);
 }
 @override
 public list<t> sublist(int fromindex, int toindex) {
 return result.sublist(fromindex, toindex);
 }
}

二.提供dao以及mapper.xml

dao的写法:

page<model> pagebyconditions(rowbounds rowbounds, model record);

mapper.xml:

<!-- 表名 -->
 <sql id="tablename" >
 model
 </sql>
 <!-- 数据表所有列名 -->
 <sql id="base_column_list" >
 id, 
 name 
 </sql>
 <!-- 查询字段 -->
 <sql id="base_search_param" >
 <if test="id != null" >
  and id = #{id,jdbctype=integer}
 </if>
 <if test="name != null" >
  and name = #{name,jdbctype=varchar}
 </if>
 </sql>
 <!-- 分页查询语句 -->
 <select id="pagebyconditions" resultmap="baseresultmap">
 select 
  <include refid="base_column_list" />
 from 
  <include refid="tablename" />
 where 1=1
  <include refid="base_search_param" />
 </select>

ok,以上都是mybatis的基本操作,就不做多余解释。

三.创建拦截器:

我们需要做的是创建一个拦截器(pageinterceptor)、一个执行者(pageexecutor)。

1.pageinteceptor:实现inteceptor接口,将pageexecutor进行执行,拦截sql添加分页sql(limit xx,xx)

2.pageexecutor:实现executor接口,在查询时,添加查询总数并修改返回值类型。因为要做的是分页,是查询操作,所以里边的非查询方法都使用基本的实现,只修改两个query方法。

pageinteceptor完整代码:

import java.lang.reflect.invocationtargetexception;
import java.sql.connection;
import java.util.properties;
import org.apache.ibatis.executor.executor;
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.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.session.resulthandler;
import org.apache.ibatis.session.rowbounds;
@intercepts({
 @signature(method = "query", type = executor.class, args = { mappedstatement.class, object.class,
  rowbounds.class, resulthandler.class }),
 @signature(method = "prepare", type = statementhandler.class, args = { connection.class }) })
public class pageinterceptor implements interceptor {
 private static final objectfactory default_object_factory = new defaultobjectfactory();
 private static final objectwrapperfactory default_object_wrapper_factory = new defaultobjectwrapperfactory();
 private string pattern = "^.*page.*$"; // 需要进行分页操作的字符串正则表达式
 public string getpattern() {
 return pattern;
 }
 public void setpattern(string pattern) {
 this.pattern = pattern;
 }
 @override
 public object intercept(invocation invocation) throws throwable {
 if (invocation.gettarget() instanceof statementhandler) {
  return handlestatementhandler(invocation);
 }
 return invocation.proceed();
 }
 /**
 * @param invocation
 * @return
 * @throws illegalaccessexception 
 * @throws invocationtargetexception 
 */
 private object handlestatementhandler(invocation invocation)
  throws invocationtargetexception, illegalaccessexception {
 statementhandler statementhandler = (statementhandler) invocation
  .gettarget();
 metaobject metastatementhandler = metaobject.forobject(
  statementhandler, default_object_factory,
  default_object_wrapper_factory);
 rowbounds rowbounds = (rowbounds) metastatementhandler
  .getvalue("delegate.rowbounds");
 if (rowbounds == null || (rowbounds.getoffset() == rowbounds.no_row_offset && rowbounds
  .getlimit() == rowbounds.no_row_limit)) {
  return invocation.proceed();
 }
 // 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环可以分离出最原始的的目标类)
 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);
 }
 // 将mybatis的内存分页,调整为物理分页
 boundsql boundsql = (boundsql) metastatementhandler.getvalue("delegate.boundsql");
 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);
 // 将执行权交给下一个拦截器
 return invocation.proceed();
 }
 @override
 public object plugin(object o) {
 if (executor.class.isassignablefrom(o.getclass())) {
  pageexecutor executor = new pageexecutor((executor)o, pattern);
  return plugin.wrap(executor, this);
 } else if (o instanceof statementhandler) {
  return plugin.wrap(o, this);
 }
 return o;
 }
 @override
 public void setproperties(properties properties) {
 }
}

pageexecutor完整代码:

import java.sql.connection;
import java.sql.preparedstatement;
import java.sql.resultset;
import java.sql.sqlexception;
import java.util.list;
import java.util.regex.matcher;
import java.util.regex.pattern;
import org.apache.ibatis.cache.cachekey;
import org.apache.ibatis.executor.batchresult;
import org.apache.ibatis.executor.executor;
import org.apache.ibatis.mapping.boundsql;
import org.apache.ibatis.mapping.mappedstatement;
import org.apache.ibatis.reflection.metaobject;
import org.apache.ibatis.session.resulthandler;
import org.apache.ibatis.session.rowbounds;
import org.apache.ibatis.transaction.transaction;
public class pageexecutor implements executor {
 private executor executor;
 private string pattern;
 public pageexecutor(executor executor, string pattern) {
 this.executor = executor;
 this.pattern = pattern;
 }
 @override
 public int update(mappedstatement ms, object parameter) throws sqlexception {
 return executor.update(ms, parameter);
 }
 @override
 public <e> list<e> query(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler,
  cachekey cachekey, boundsql boundsql) throws sqlexception {
 rowbounds rb = new rowbounds(rowbounds.getoffset(), rowbounds.getlimit());
 list<e> rows = executor.query(ms, parameter, rowbounds, resulthandler,
  cachekey, boundsql);
 return pageresolver(rows, ms, parameter, rb);
 }
 /**
 * 修改返回值类型
 * @param rows
 * @param ms
 * @param parameter
 * @param rowbounds
 * @return
 */
 private <e> list<e> pageresolver(list<e> rows, mappedstatement ms,
  object parameter, rowbounds rowbounds) {
 string msid = ms.getid();
 // 如果需要分页查询,修改返回类型为page对象
 if (msid.matches(pattern)) {
  int count = getcount(ms, parameter);
  int offset = rowbounds.getoffset();
  int pagesize = rowbounds.getlimit();
  return new page<e>(offset/pagesize + 1, pagesize, count, rows);
 }
 return rows;
 }
 /**
 * 获取总数
 * @param ms
 * @param parameter
 * @return
 */
 private int getcount(mappedstatement ms, object parameter) {
 boundsql bsql = ms.getboundsql(parameter);
 string sql = bsql.getsql();
 string countsql = getcountsql(sql);
 connection connection = null;
 preparedstatement stmt = null;
 resultset rs = null;
 try {
  connection = ms.getconfiguration().getenvironment().getdatasource()
   .getconnection();
  stmt = connection.preparestatement(countsql);
  rs = stmt.executequery();
  if (rs.next())
  return rs.getint(1);
 } catch (sqlexception e) {
  e.printstacktrace();
 } finally {
  try {
  if (connection != null && !connection.isclosed()) {
   connection.close();
  }
  } catch (sqlexception e) {
  e.printstacktrace();
  }
 }
 return 0;
 }
 private string getcountsql(string sql) {
 string counthql = " select count(*) "
  + removeselect(removeorders(sql));

 return counthql;
 }
 protected string removeorders(string sql) {
 pattern p = pattern.compile("order\\s*by[\\w|\\w|\\s|\\s]*", pattern.case_insensitive);
 matcher m = p.matcher(sql);
 stringbuffer sb = new stringbuffer();
 while (m.find()) {
  m.appendreplacement(sb, "");
 }
 m.appendtail(sb);
 return sb.tostring();
 }
 // 去除sql语句中select子句
 private static string removeselect(string hql) {
 int beginpos = hql.tolowercase().indexof("from");
 if (beginpos < 0) {
  throw new illegalargumentexception(" hql : " + hql + " must has a keyword 'from'");
 }
 return hql.substring(beginpos);
 }
 @override
 public <e> list<e> query(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler)
  throws sqlexception {
 boundsql boundsql = ms.getboundsql(parameter);
 return query(ms, parameter, rowbounds, resulthandler,
  executor.createcachekey(ms, parameter, rowbounds, boundsql),
  boundsql);
 }
 @override
 public list<batchresult> flushstatements() throws sqlexception {
 return executor.flushstatements();
 }
 @override
 public void commit(boolean required) throws sqlexception {
 executor.commit(required);
 }
 @override
 public void rollback(boolean required) throws sqlexception {
 executor.rollback(required);
 }
 @override
 public cachekey createcachekey(mappedstatement ms, object parameterobject,
  rowbounds rowbounds, boundsql boundsql) {
 return executor
  .createcachekey(ms, parameterobject, rowbounds, boundsql);
 }
 @override
 public boolean iscached(mappedstatement ms, cachekey key) {
 return executor.iscached(ms, key);
 }
 @override
 public void clearlocalcache() {
 executor.clearlocalcache();
 }
 @override
 public void deferload(mappedstatement ms, metaobject resultobject,
  string property, cachekey key, class<?> targettype) {
 executor.deferload(ms, resultobject, property, key, targettype);
 }
 @override
 public transaction gettransaction() {
 return executor.gettransaction();
 }
 @override
 public void close(boolean forcerollback) {
 executor.close(forcerollback);
 }
 @override
 public boolean isclosed() {
 return executor.isclosed();
 }
}

关于page需要实现list接口的原因:可以看到,query方法返回值是list<e>,而我们现在要在dao中使用page<e>对象来接收mybatis返回的结果,所以需要让page实现list接口。

分页查询执行顺序:进入pageinterceptor的plugin方法,拦截到执行者,进入pageexecutor的query方法,执行executor.query()时,又再次回到pageinterceptor的plugin方法,这次会执行

进入intercept方法,将执行的sql拼接上分页限制语句,然后查询出数据结果集合。executor.query()执行完成后,继续执行pageresolver,如果方法名称和配置的需要执行分页操作的字符串匹配时,查询数据总量,并返回page对象;如果不匹配,直接返回list对象。

四.xml配置:

<bean id="sqlsessionfactory" class="org.mybatis.spring.sqlsessionfactorybean">
 <property name="datasource" ref="datasource" />
 <property name="configlocation" value="classpath:/conf/mybatis/mybaties-config.xml"></property>
 <property name="mapperlocations">
  <list>
  <value>classpath:/conf/mybatis/**/*-mapper.xml</value>
  </list>
 </property>
 <property name="plugins">
  <list>
  <ref bean="pageinterceptor"/>
  </list> 
 </property>
 </bean>
 <bean id="pageinterceptor" class="cn.com.common.pageinterceptor">
 <property name="pattern" value="^.*page.*$"></property>
 </bean>

五.测试代码:

@test
 public void testpage() {
 int pageno = 1;
 int pagesize = 10;
 rowbounds bounds = new rowbounds((pageno - 1) * pagesize, pagesize);
 model record = new model();
 page<model> list = modelmapper.pagebyconditions(bounds, record);
 }

本文主要介绍了mybatis拦截器实现分页的步骤与方法。具有很好的参考价值,下面跟着小编一起来看下吧

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网