Mybatis插件原理分析(三)分页插件
来源:互联网 发布:pl sql insert into 编辑:程序博客网 时间:2024/06/01 07:23
在Mybatis中插件最经常使用的是作为分页插件,接下来我们通过实现Interceptor来完成一个分页插件。
虽然Mybatis也提供了分页操作,通过在sqlSession的接口函数中设置RowBounds,给RowBounds设置初值(RowBounds源码)来实现逻辑分页,其实现原理就是通过sql查询所有的结果,并将结果放到List中,然后根据RowBouds的limit和offset数值来返回最后的数据,这种逻辑分页在数据量比较大的情况下对性能是有影响的。虽然我们可以自己写带有分页语句的sql来实现物理分页,如mysql下:select * from table limit 10 offset 1,来获得分页结果,但不同的数据库产品(mysql、oracle、sqlserver和oracle)对应的分页语句并不相同,如果要同时兼容所有的数据库产品,需要开发人员在每个分页sql中都编码几种数据库产品的分页语句,这样的开发效率实在是太低了,Mybatis的插件Interceptor给了我们一种根据数据库类型自动组装sql语句的方法。
PageInterceptor源码如下:
package com.tianjunwei.page;import java.lang.reflect.Constructor;import java.sql.Connection;import java.sql.DatabaseMetaData;import java.util.List;import java.util.Properties;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.ParameterMapping;import org.apache.ibatis.mapping.SqlSource;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.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.apache.ibatis.mapping.MappedStatement.Builder;import com.tianjunwei.page.dialect.Dialect;import com.tianjunwei.page.dialect.DialectFactory;/* * 分页插件我们只需要拦截Executor的query方法即可,在执行sql语句之前组装新的分页sql语句 */@Intercepts({@Signature(type= Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class PageInterceptor implements Interceptor{ String dialectClass; boolean asyncTotalCount = false; String dataBaseType=null; public static ThreadLocal<RowBounds> PageRowBounds = new ThreadLocal<RowBounds>();@SuppressWarnings({"rawtypes", "unchecked"})public Object intercept(final Invocation invocation) throws Throwable {//Executor的实现类 final Executor executor = (Executor) invocation.getTarget(); //Executor的query函数的参数 final Object[] queryArgs = invocation.getArgs(); final MappedStatement ms = (MappedStatement)queryArgs[0]; final Object parameter = queryArgs[1]; //rowBounds中有分页语句的limit和offset值 RowBounds rowBounds = (RowBounds)queryArgs[2]; if((PageRowBounds.get() != null)&&(PageRowBounds.get().getLimit() != RowBounds.NO_ROW_LIMIT || PageRowBounds.get().getOffset() != RowBounds.NO_ROW_OFFSET)){ rowBounds = PageRowBounds.get(); } //如果不需要分页操作,直接返回,rowBounds为默认值时 if(rowBounds.getOffset() == RowBounds.NO_ROW_OFFSET && rowBounds.getLimit() == RowBounds.NO_ROW_LIMIT){ return invocation.proceed(); } //根据不同的数据库获取不到的分页方言来 if(dialectClass == null || "".equals(dialectClass)){ //判断数据源选择方言,暂时支持mysql、oracle、postgresql和sql server 2005 2008及2012 Connection connection = executor.getTransaction().getConnection(); DatabaseMetaData databaseMetaData = null; if(connection != null){ databaseMetaData = connection.getMetaData(); }else {throw new Exception("connection is null");} String databaseProductName = databaseMetaData.getDatabaseProductName(); if( dataBaseType == null || "".equals(dataBaseType)){ dataBaseType = databaseProductName; } //通过xml方言的配置来获得方言类 if(databaseProductName != null && !("".equals(dataBaseType))){ dialectClass = DialectFactory.getDialectClass(dataBaseType,databaseProductName); }else{ throw new Exception("the property of dialect is null"); } setDialectClass(dialectClass); } final Dialect dialect; try { //初始化分页方言类 Class clazz = Class.forName(dialectClass); Constructor constructor = clazz.getConstructor(new Class[]{MappedStatement.class, Object.class, RowBounds.class}); dialect = (Dialect)constructor.newInstance(new Object[]{ms, parameter, rowBounds}); } catch (Exception e) { throw new ClassNotFoundException("Cannot create dialect instance: "+dialectClass,e); } final BoundSql boundSql = ms.getBoundSql(parameter); //创建新的MappedStatement,此时的sql语句已经是符合数据库产品的分页语句 //dialect.getPageSQL()获得分页语句 //dialect.getParameterMappings(), dialect.getParameterObject(),添加了两个参数及其值,两个参数为_limit和_offset queryArgs[0] = copyFromNewSql(ms,boundSql,dialect.getPageSQL(), dialect.getParameterMappings(), dialect.getParameterObject()); //sql语句的参数集合 queryArgs[1] = dialect.getParameterObject(); //设置为不分页,由新的sql语句进行物理分页 queryArgs[2] = new RowBounds(RowBounds.NO_ROW_OFFSET,RowBounds.NO_ROW_LIMIT); return invocation.proceed();}//这个方法是用于mybatis接口编程过程中显示的指定分页参数public static void setPage(int pageNumber,int pageSize){RowBounds pageRowBounds = null;if(pageNumber > 0)pageRowBounds = new RowBounds((pageNumber-1)*pageSize, pageSize);else {pageRowBounds = new RowBounds(0, pageSize);}PageRowBounds.set(pageRowBounds);}//创建新的MappedStatement private MappedStatement copyFromNewSql(MappedStatement ms, BoundSql boundSql, String sql, List<ParameterMapping> parameterMappings, Object parameter){ //根据新的分页sql语句创建BoundSql BoundSql newBoundSql = copyFromBoundSql(ms, boundSql, sql, parameterMappings, parameter); //根据newBoundSql创建新的MappedStatement return copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql)); } //根据新的分页sql语句创建BoundSqlprivate BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql,String sql, List<ParameterMapping> parameterMappings,Object parameter) {BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),sql, parameterMappings, parameter);for (ParameterMapping mapping : boundSql.getParameterMappings()) { String prop = mapping.getProperty(); if (boundSql.hasAdditionalParameter(prop)) { newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop)); }}return newBoundSql;}//根据newBoundSql创建新的MappedStatementprivate MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {Builder builder = new Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.keyGenerator(ms.getKeyGenerator());if(ms.getKeyProperties() != null && ms.getKeyProperties().length !=0){ StringBuffer keyProperties = new StringBuffer(); for(String keyProperty : ms.getKeyProperties()){ keyProperties.append(keyProperty).append(","); } keyProperties.delete(keyProperties.length()-1, keyProperties.length());builder.keyProperty(keyProperties.toString());}//setStatementTimeout()builder.timeout(ms.getTimeout());//setStatementResultMap()builder.parameterMap(ms.getParameterMap());//setStatementResultMap() builder.resultMaps(ms.getResultMaps());builder.resultSetType(ms.getResultSetType());//setStatementCache()builder.cache(ms.getCache());builder.flushCacheRequired(ms.isFlushCacheRequired());builder.useCache(ms.isUseCache());return builder.build();}public Object plugin(Object target) {return Plugin.wrap(target, this);}/** * @Title: setProperties * @Description: 方言插件配置时设置的参数 * @param properties 参数 * @return void * @2016年1月13日下午3:54:47 */public void setProperties(Properties properties) {dataBaseType = properties.getProperty("dialectType");}public static class BoundSqlSqlSource implements SqlSource {BoundSql boundSql;public BoundSqlSqlSource(BoundSql boundSql) {this.boundSql = boundSql;}public BoundSql getBoundSql(Object parameterObject) {return boundSql;}} public void setDialectClass(String dialectClass) { this.dialectClass = dialectClass; } public void setDataBaseType(String dataBaseType) {this.dataBaseType = dataBaseType;} }
详细代码及工程请查看:https://github.com/IAMADOG/TTmybatis
0 0
- Mybatis插件原理分析(三)分页插件
- Mybatis插件原理分析(一)
- Mybatis插件原理分析(二)
- Mybatis使用pageHelper分页插件原理
- Mybatis使用pageHelper分页插件原理
- mybatis使用PageHelper分页插件原理
- Mybatis使用pageHelper分页插件原理
- mybatis分页/分页插件
- Mybatis插件原理和PageHelper结合实战分页插件
- MyBatis拦截器分页原理及PageHelper分页插件教学
- 【MyBatis源码分析】插件实现原理
- Mybatis分页插件
- Mybatis分页插件 - 示例
- Mybatis分页插件更新
- mybatis generator 分页插件
- Mybatis分页插件更新
- Mybatis分页插件 - 示例
- MyBatis分页插件
- Android数据通信——JSON&Gson(Android studio)
- PL/SQL for loop
- Sentry Robots, ACM/ICPC SWERC 2012, UVa12549 【二分图】
- hdu 5441 离线处理+并查集
- 顺序表应用7:最大子段和之分治递归法
- Mybatis插件原理分析(三)分页插件
- 51nod 1079 中国剩余定理
- 欢迎使用CSDN-markdown编辑器
- 进程状态
- maven导出项目依赖的jar包
- 时钟六:在LCD1602上显示时钟
- contentSize、contentInset和contentOffset
- 北理工本科生管理系统成绩查询爬虫
- 【机器学习】关联规则与Apriori算法