Mybatis拦截器实现分页
最终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拦截器实现分页的步骤与方法。具有很好的参考价值,下面跟着小编一起来看下吧
上一篇: 21. IO流(字符流)
下一篇: Java+mysql用户注册登录功能