mybatis simple pagination plugin

The SqlSession of mybatis can perform paging query directly through the following interfaces:

<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

 However, the query is based on a way of subsetting the query results. This method is less efficient (if only one data server needs to return all the data that meets the conditions).

 

Implementation idea: use the mybatis plug-in to intercept the prepare method of StatementHandler, modify the Sql statement, and add paging query statements. As for the total number of pages in the pagination query, another select count (*) query statement is executed. The implementation is simpler.

Introduction to the execution process of StatementHandler:

Create a StatementHandler in the Executor with the following code :

 

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

 Configuration is the global configuration file of mybatis, and its newStatementHandler creation process is as follows:

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

 RoutingStatementHandler can be regarded as an illegal statement handler, and the specific operation is still delegated to a specific BaseStatementHandler. interceptorChain.pluginAll is used to apply all configured myabtis plugin classes to this interface. interceptorChain is an interceptor chain (chain of responsibility pattern)

 

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

 Interceptors save all the interceptors configured with mybatis, and call the interceptors to proxy the target respectively. That is, the proxy that generates the target class. For example the interceptor Incet

package cn.liuyao.mybatis.test;

import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
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;

@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {java.sql.Connection.class})})
public class Incet implements Interceptor{

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		return invocation.proceed();
	}

	//Generate the proxy of the target object
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}
}

 The interceptor's plugin method calls the Plugin's wrap to wrap the proxy on the target object.

  public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }

 wrap returns the proxy of the target object, so that when executing a specific method of a specific interface, you can add some of your own code, similar to spring's aop function.

Object intercept(Invocation invocation)

intercept to write additional code. Implement your own paging logic.

 Since the statement execution of mybatis is finally completed by the StatementHandler, as long as the prepare method of the interface is intercepted, and boundSql and rowBound are modified, the effect of modifying Sql and adding paging information can be achieved.

Page class:

 

package com.ccq.framework.lang;

import java.io.Serializable;

public class Page implements Serializable{

	private static final long serialVersionUID = 9000151198919642793L;

	private int pages;
	private int pageNum;
	private int pageSize;
	
	public Page() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Page(int pages, int pageNum, int pageSize) {
		super();
		this.pages = pages;
		this.pageNum = pageNum;
		this.pageSize = pageSize;
	}
	public int getPages() {
		return pages;
	}
	public void setPages(int pages) {
		this.pages = pages;
	}
	public int getPageNum() {
		return pageNum;
	}
	public void setPageNum(int pageNum) {
		this.pageNum = pageNum;
	}
	public int getPageSize() {
		return pageSize;
	}
	public void setPageSize(int pageSize) {
		this.pageSize = pageSize;
	}
}
 The page class is used to save paging information: current page, page size, and number of all pages.

 

pagePlugin:

package com.ccq.framework.plugin;

import java.util.Properties;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
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.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {java.sql.Connection.class})})
public class MyPagePlugin implements Interceptor{

	private Dialect dialect;
	
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		
		Logger log = LoggerFactory.getLogger(MyPagePlugin.class);
		
		StatementHandler hander = (StatementHandler) invocation.getTarget();
		//Wrap the handler to get its private properties
		MetaObject meta = MetaObject.forObject(hander, new DefaultObjectFactory(),
				new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
		RowBounds rowBound = (RowBounds) meta.getValue("delegate.rowBounds");//Get paging information
		
		if(rowBound.equals(RowBounds.DEFAULT)) {
			
			// unspecified paging command
			return invocation.proceed();
		}
		BoundSql boundSql = (BoundSql) meta.getValue("delegate.boundSql");//获取sql语句
		
		if(dialect == null) {
			
			log.debug("not assigned dialect,use mysql default");
			dialect = new MysqlDialect();
		}
		
		String buildSql = dialect.builderSql(boundSql.getSql(), rowBound.getOffset(), rowBound.getLimit());
		MetaObject boundSqlMeta = MetaObject.forObject(boundSql, new DefaultObjectFactory(),
				new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
		
		boundSqlMeta.setValue("sql", buildSql);
		
		meta.setValue("delegate.rowBounds", RowBound.DEFAULT);//Reply to default paging information
		return invocation.proceed();
	}

	//Generate the proxy of the target object
	@Override
	public Object plugin(Object target) {
		if(target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		}else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {
		if(properties.getProperty("dialect").equals("mysql")) {
			dialect = new MysqlDialect();
		}else if(properties.getProperty("dialect").equals("oracle")) {
			
			//EVERYTHING
		}
	}

}
 buildSql:

 

package com.ccq.framework.plugin;

import com.ccq.framework.exception.AppException;

public class MysqlDialect extends Dialect{

	@Override
	public String builderSql(String rawSql,int OFFSET,int LIMIT) {
		
		if(rawSql.endsWith(";")) {
			
			throw new AppException("Bad sql: grammer error on ; with limit");
		}
		
		StringBuffer sb = new StringBuffer(rawSql);
		//SELECT * FROM table LIMIT [offset,] rows
		sb.append(String.format(" limit %s,%s",new Object[]{OFFSET,LIMIT}));
		return sb.toString();
	}
}

 Only for mysql sql statement.

 

Test: The general single-table CRUD tool of mybatis is used, and the link is given later.

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327016083&siteId=291194637