Spring Mybatis 分页插件使用教程
mybatis分页切入点
mybatis内部有个plugins(插件)概念,本质上属于拦截器的思想。具体的解析可见他文mybatis拦截器原理探究。本文将在此基础上直接展示实际项目的实现代码和其他的相关解析
分页具体代码实现
首先我们可以定义方言抽象类,用于实现分页abstractdialect.java
public abstract class abstractdialect{ /** * 是否支持limit和偏移量 * @return */ public abstract boolean supportslimitoffset(); /** * 是否支持limit * @return */ public abstract boolean supportslimit(); /** * 获取增加了分页属性之后的sql * @param sql * @param offset * @param limit * @return */ public abstract string getlimitstring(string sql, int offset, int limit); }
再而我们就以oracle与mysql数据库的分页技术作下分别的实现
mysqldialect.java-mysql分页方言
public class mysqldialect extends abstractdialect { public boolean supportslimitoffset() { return true; } public boolean supportslimit() { return true; } public string getlimitstring(string sql, int offset, int limit) { if (offset > 0) { return sql + " limit " + offset + "," + limit; } else { return sql + " limit " + limit; } } }
oracledialect.java-oracle方言实现
public class oracledialect extends adialect{ @override public boolean supportslimitoffset() { return false; } @override public boolean supportslimit() { return false; } @override public string getlimitstring(string sql, int start, int limit) { if(start < 0){ start = 0; } if(limit < 0){ limit = 10; } stringbuilder pagesql = new stringbuilder(100); pagesql.append("select * from ( select temp.*, rownum row_id from ( "); pagesql.append(sql); pagesql.append(" ) temp where rownum <= ").append(start+limit); pagesql.append(") where row_id > ").append(start); return pagesql.tostring(); } }
对应的mybatis插件拦截器实现如下,拦截statementhandler#prepare(connection con)创建sql语句对象方法
paginationinterceptor.java
@intercepts({ @signature(type = statementhandler.class, method = "prepare", args = { connection.class }) }) public final class paginationinterceptor implements interceptor { private final static logger log = loggerfactory .getlogger(paginationinterceptor.class); private adialect dialect; public void setdialect(adialect dialect) { this.dialect = dialect; } @override public object intercept(invocation invocation) throws throwable { // 直接获取拦截的对象,其实现类routingstatementhandler statementhandler statementhandler = (statementhandler) invocation .gettarget(); boundsql boundsql = statementhandler.getboundsql(); // 获取元对象,主要用于获取statementhandler所关联的对象及属性 metaobject metastatementhandler = metaobject.forobject( statementhandler, new defaultobjectfactory(), new defaultobjectwrapperfactory()); mappedstatement mappedstmt= (mappedstatement) metastatementhandler .getvalue("delegate.mappedstatement".intern()); // 只对querypagination()方法进行分页操作 if(mappedstmt.getid().indexof("querypagination")==-1){ return invocation.proceed(); } // 重新构造分页的sql string originalsql = (string) metastatementhandler .getvalue("delegate.boundsql.sql".intern()); metastatementhandler.setvalue("delegate.boundsql.sql".intern(), dialect .getlimitstring(originalsql, rowbounds.getoffset(), rowbounds.getlimit())); metastatementhandler.setvalue("delegate.rowbounds.offset".intern(), rowbounds.no_row_offset); metastatementhandler.setvalue("delegate.rowbounds.limit".intern(), rowbounds.no_row_limit); log.debug("page sql : " + boundsql.getsql()); return invocation.proceed(); } // 拦截对象 @override public object plugin(object target) { return plugin.wrap(target, this); } @override public void setproperties(properties properties) { } }
spring对应的xml配置可如下,以oracle分页为例子
<!-- oracle方言配置,用于oracle的分页 --> <bean id="paginationinterceptor" class="com.hsnet.winner.cas.admin.core.dao.mybatis.interceptor.paginationinterceptor"> <property name="dialect"> <bean class="cn.cloud.winner.oss.manager.mybatis.page.oracledialect" /> </property> </bean>
使用以上的代码以及配置即可完成对oracle数据库以及mysql数据库的分页操作。并且博主对其中的某个点作下解析
mybatis#metaobject-元数据对象解析
以上的代码博主之前在使用的时候,对其中的metaobject这个类很费解,其直接通过getvalue()方法便可以将所代理的对象的所关联的属性全都拿取到。我们可以跟随源码深入了解下
metaobject#forobject()
代理对象均通过此静态方法进入
public static metaobject forobject(object object, objectfactory objectfactory, objectwrapperfactory objectwrapperfactory) { if (object == null) { return systemmetaobject.null_meta_object; } else { return new metaobject(object, objectfactory, objectwrapperfactory); } }
我们可以直接观察其中的构造函数,玄机就在此处
private metaobject(object object, objectfactory objectfactory, objectwrapperfactory objectwrapperfactory) { this.originalobject = object; this.objectfactory = objectfactory; this.objectwrapperfactory = objectwrapperfactory; // 所有的属性获取均通过objectwrapper类来获取,此处主要对所代理的object对象类型进行判断 if (object instanceof objectwrapper) { this.objectwrapper = (objectwrapper) object; } else if (objectwrapperfactory.haswrapperfor(object)) { this.objectwrapper = objectwrapperfactory.getwrapperfor(this, object); } else if (object instanceof map) { this.objectwrapper = new mapwrapper(this, (map) object); } else if (object instanceof collection) { this.objectwrapper = new collectionwrapper(this, (collection) object); } else { // 我们常用的便是beanwrapper this.objectwrapper = new beanwrapper(this, object); } }
为了理解的更为渗透,我们继续跟进,最后我们得知其会调用reflector类的构造函数
private reflector(class<?> clazz) { type = clazz; // 获取构造类 adddefaultconstructor(clazz); // 获取get方法集合 addgetmethods(clazz); // 获取set方法集合 addsetmethods(clazz); // 获取内部属性集合 addfields(clazz); readablepropertynames = getmethods.keyset().toarray(new string[getmethods.keyset().size()]); writeablepropertynames = setmethods.keyset().toarray(new string[setmethods.keyset().size()]); for (string propname : readablepropertynames) { caseinsensitivepropertymap.put(propname.touppercase(locale.english), propname); } for (string propname : writeablepropertynames) { caseinsensitivepropertymap.put(propname.touppercase(locale.english), propname); } }
由此我们便可知使用reflector代理类以及metaobject便可以遍历代理被代理类的所关联的所有属性,就拿routingstatementhandler
类来说,经过上述操作后其便可以访问内部属性delegate以及delegate的内部属性configuration/objectfactory/typehandlerregistry/resultsethandler/parameterhandler/mappedstatement
等属性
metaobject#getvalue()
上述阐述的是如何代理被代理类的内部属性,我们也简单的看下是如何正确的调用
public object getvalue(string name) { // propertytokenizer与stringtokenizer类似,只是前者写死以.为分隔符 propertytokenizer prop = new propertytokenizer(name); if (prop.hasnext()) { metaobject metavalue = metaobjectforproperty(prop.getindexedname()); if (metavalue == systemmetaobject.null_meta_object) { return null; } else { return metavalue.getvalue(prop.getchildren()); } } else { return objectwrapper.get(prop); } }
具体的解析就不在此阐述了,如何用户想获取statementhandler所拥有的sql字符串,可通过getvalue("delegate.boundsql.sql")
其中以.为分隔符并其中的属性必须是内部属性(区分大小写)。
metaobject#setvalue()
原理同metaobject#getvalue()
方法
总结
以上所述是小编给大家介绍的spring mybatis 分页插件使用教程,希望对大家有所帮助
上一篇: Vue学习之路第七篇:跑马灯项目实现