功能特性
分页插件实现流程
补充:参数对象,单个参数是其本身,多个参数为Map
分页插件功能实现
1、定义Page类
package com.laoxu.mybatis.executor.plugin;
public class Page {
public Page(int size, int index) {
this.size = size;
this.index = index;
}
private int total; // 总行数
private int size; // 每页大小
private int index; // 页码
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getIndex() {
return size * (index - 1); // offset 0 limit 50<size>
}
public void setIndex(int index) {
this.index = index;
}
}
2、编写拦截器
package com.laoxu.mybatis.executor.plugin;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.Properties;
/**
* 分页拦截器
*/
@Intercepts(
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}))
public class PageInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 1、检查当前是否满足分页条件
// 带上分页参数
StatementHandler target = (StatementHandler) invocation.getTarget();
// Sql包 sql,参数,参数映射
BoundSql boundSql = target.getBoundSql();
Object parameterObject = boundSql.getParameterObject();
Page page = null;
if(parameterObject instanceof Page){
page = (Page) parameterObject;
}else if (parameterObject instanceof Map){
page = (Page)((Map) parameterObject).values().stream().filter(v->v instanceof Page).findFirst().orElse(null);
}
if(page==null){
return invocation.proceed();
}
// 2、设置总行数
// select count(0) from (sql);
page.setTotal(selectCount(invocation));
// 3、修改原sql
// select * from person offset 0 , limit 50
String newSql = String.format("%s limit %s offset %s", boundSql.getSql(), page.getSize(), page
.getIndex());
SystemMetaObject.forObject(boundSql).setValue("sql", newSql);
return invocation.proceed();
}
private int selectCount(Invocation invocation) throws SQLException {
int count = 0;
StatementHandler target = (StatementHandler) invocation.getTarget();
// Sql包 sql,参数,参数映射
String countSql = String.format("select count(*) from (%s) as _page", target.getBoundSql().getSql());
// JDBC
Connection connection = (Connection) invocation.getArgs()[0];
PreparedStatement preparedStatement = connection.prepareStatement(countSql);
target.getParameterHandler().setParameters(preparedStatement);
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
count = resultSet.getInt(1);
}
resultSet.close();
preparedStatement.close();
return count;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
- 1、继承mybatis的拦截Interceptor类
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.plugin;
import java.util.Properties;
/**
* @author Clinton Begin
*/
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
Object intercept(Invocation invocation) throws Throwable; //主要实现拦截的主逻辑
Object plugin(Object target) //插件包装
Plugin.wrap(target, this);
动态代理的方式对@Intercepts注解及拦截器进行包装解析
void setProperties(Properties properties) //设置属性
- 2、添加拦截器注解@Intercepts
@Intercepts(
@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class}))
type 指定要拦截的处理器类
method 指定拦截处理器类李的方法
args 指定需要用到的参数
以上拦截器使用到了第一个参数,这个参数来自 StatementHandler类的prepare方法传入的参数
Connection connection = (Connection) invocation.getArgs()[0];
查看StatementHandler类的内容
/**
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.statement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.session.ResultHandler;
/**
* @author Clinton Begin
*/
public interface StatementHandler {
// 创建Statement,基于java.sql
Statement prepare(Connection connection, Integer transactionTimeout)
throws SQLException;
// 设置参数
void parameterize(Statement statement)
throws SQLException;
// 执行批处理
void batch(Statement statement)
throws SQLException;
// 执行修改
int update(Statement statement)
throws SQLException;
// 执行查询
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
// 查询游标
<E> Cursor<E> queryCursor(Statement statement)
throws SQLException;
// 获取动态sql
BoundSql getBoundSql();
// 获取参数处理器
ParameterHandler getParameterHandler();
}
实现类部分代码:
/**
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.statement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* @author Clinton Begin
*/
public abstract class BaseStatementHandler implements StatementHandler {
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
}
3、执行测试
package com.laoxu.mybatis.executor.plugin;
import com.laoxu.mybatis.mapper.PersonMapper;
import com.laoxu.mybatis.modal.Person;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Test {
public static void main(String[] args) throws IOException {
test1();
}
private static void test1() throws IOException {
String resource = "mapper/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try (SqlSession session = sqlSessionFactory.openSession()) {
PersonMapper personMapper = session.getMapper(PersonMapper.class);
// 设置分页
Page page = new Page(2, 1);
List<Person> personList = personMapper.getAllPersonsByPage(page);
System.out.println("返回数量:" + personList.size());
System.out.println("总数:" + page.getTotal());
System.out.println(personList);
}
}
}
返回结果:
==> Preparing: select count(*) from (select * from person) as _page ==> Parameters: <== Columns: count(*) <== Row: 3 ==> Preparing: select * from person limit 2 offset 0 ==> Parameters: <== Columns: id, name, sex, age, create_time <== Row: 20210420172645864088a2a1ba11eb886d525400146075, 李四, 1, 20, 2021-04-20 17:26:45 <== Row: 20210420172922e412b7bea1ba11eb886d525400146075, 张三, 1, 20, 2021-04-20 17:29:22 <== Total: 2 返回数量:2 总数:3