¿Cómo implementar PageHelper?
- PageHelper primero guarda los parámetros pasados por el front-end al objeto de la página, y luego almacena una copia de la página en ThreadLoacl, para garantizar que los parámetros no se afecten entre sí al paginar.
- Luego use el interceptor provisto por mybatis para obtener el valor de ThreadLocal, vuelva a ensamblar el SQL de paginación, agregue el parámetro de paginación en la instrucción sql a través del interceptor al ejecutar la consulta y luego implemente la consulta de paginación.
- Finalmente, los objetos almacenados en ThreadLocal se borran automáticamente en el segmento de código final.
Paso 1: Guardar página en ThreadLocal
public abstract class PageMethod {
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal();
...
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
//page对象存储了如pageNum、pageSize、orderBy等查询条件
Page<E> page = new Page(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
Page<E> oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page);//存储到ThreadLocal中
return page;
}
protected static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
Depurar en esta posición, de hecho, Page.startpage ha completado lo que debe hacer, el siguiente paso es ejecutar selectByPrimaryKey()
este método.
El segundo paso: Interceptor de inyección SqlSessionFactory
Pasando a la siguiente oración de Debug selectByPrimaryKey()
, llegamos a esta clase:
public class MapperProxy<T> implements InvocationHandler, Serializable {
...
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
}
Independientemente del método que ocurra primero, solo mire InvocationHandler para comprender que este es un proxy dinámico.
En esta clase, Mybatis completó el proceso de inicialización:
el proceso central de inicialización es leer el archivo de configuración en la instancia de Congiguration, y luego generar una instancia pública global SqlSessionTemplate para SqlSessionFactory.
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
...
private Interceptor[] plugins;
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
...
if (!ObjectUtils.isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach((plugin) -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> {
return "Registered plugin: '" + plugin + "'";
});
});
}
...
}
}
En SqlSessionFactoryBean, notamos que sus propiedades contienen una matriz de Interceptor, y el buildSqlSessionFactory()
método completa la inyección de Interceptor al agregar complementos a la Configuración.
Una vez completada la inicialización, el siguiente es el proceso de consulta de Mapper, que es una cadena de llamadas , de la siguiente manera: El
núcleo es el método configuration.newExecutor (), que cargará la cadena de intercepción, que es el pageInterceptor.
El tercer paso: PageInterceptor para lograr la paginación
La lógica central de PageHelper está en PageInterceptor, PageInterceptor es un interceptor.
public class PageInterceptor implements Interceptor {
private volatile Dialect dialect;
private String countSuffix = "_COUNT";
protected Cache<String, MappedStatement> msCountMap = null;
private String default_dialect_class = "com.github.pagehelper.PageHelper";
...
}