2019.9.24笔记——mybatis源码解析之插件开发(分页)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_35262405/article/details/101310045

分页概述

分页技术分类如下:

  • 数组分页,效率和空间占用率都很差
  • sql分页,效率和空间占用率都很好,但是编写sql会很麻烦
  • RowBounds分页(底层也是数组分页),同数组分页
  • 拦截器分页(推荐)

mybatis的四大对象

mybatis四大对象指的是:executor,statementHandler,parameterHandler和resultHandler对象。这四个对象在sqlSession内部共同协作完成sql语句的执行,同时也是我们自定义插件拦截的四大对象。

下面是mybatis查询的大体流程
在这里插入图片描述

分页插件

因为需要在发送sql语句查询前实现分页操作,所以我们需要在sql执行前将分页sql插入进去,因为statementHandler是负责执行sql语句的,所以需要拦截的对象就是statementHandler。我选择拦截的方法则是prepare,因为这个方法是在执行sql语句之前执行的。

拦截后的流程如下:

  1. 执行一条count语句
  2. 重写sql,在sql语句后面加上分页语句
  3. 将statementHandler中的sql语句替换成重写的sql
  4. 让拦截器执行下去(因为可能不止有一个拦截器)

这里约定mapper的id结尾是ByPage的才会去是实现分页功能

插件对象

import com.zdd.util.PageUtil;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;

//配置拦截的对象和方法和方法要传递的参数的类型
//args : 你需要mybatis传入什么参数给你 type :你需要拦截的对象  method=要拦截的方法
@Intercepts(@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class,Integer.class}))
public class MyPagePlugin implements Interceptor {

    String dataName = "mysql";

    String pageSqlId = ".*ByPage$";

    /**
     * 我们自己拦截器里面的逻辑
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        /**
         * 获得MetaObject对象可以不通过反射就可以拿到StatementHandler的属性
         */
        MetaObject metaObject = MetaObject.forObject(
                statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
        String sqlId = (String)metaObject.getValue("delegate.mappedStatement.id");
        //判断一下是否是分页
//        <!--第一步 执行一条couunt语句-->
        //1.1拿到连接
        //1.2 预编译SQL语句 拿到绑定的sql语句
        //1.3 执行 count语句   怎么返回你执行count的结果

//    <!--第二部 重写sql  select * from luban_product  limit start,limit -->
        //2.1 ? 怎么知道 start 和limit
        //2.2拼接start 和limit
        //2.3 替换原来绑定sql
        //拿到原来应该执行的sql
        if (sqlId.matches(pageSqlId)){

            ParameterHandler parameterHandler = statementHandler.getParameterHandler();
            //原来应该执行的sql
            String sql = statementHandler.getBoundSql().getSql();
            //sql= select * from  product    select count(0) from (select * from  product) as a
            //select * from luban_product where name = #{name}
            //执行一条count语句
            //拿到数据库连接对象
            Connection connection = (Connection) invocation.getArgs()[0];
            String countSql = "select count(0) from ("+sql+") a";
            System.out.println(countSql);
            //渲染参数
            PreparedStatement preparedStatement = connection.prepareStatement(countSql);
            //条件交给mybatis
            parameterHandler.setParameters(preparedStatement);
            ResultSet resultSet = preparedStatement.executeQuery();
            int count =0;
            if (resultSet.next()) {
                count = resultSet.getInt(1);
            }
            resultSet.close();
            preparedStatement.close();
            //获得你传进来的参数对象
            Map<String, Object> parameterObject = (Map<String, Object>) parameterHandler.getParameterObject();
            //limit  page
            PageUtil pageUtil = (PageUtil) parameterObject.get("page");
            //limit 1 ,10  十条数据   总共可能有100   count 要的是 后面的100
            pageUtil.setCount(count);

            //拼接分页语句(limit) 并且修改mysql本该执行的语句
            String pageSql = getPageSql(sql, pageUtil);
            metaObject.setValue("delegate.boundSql.sql",pageSql);
            System.out.println(pageSql);
        }
        //推进拦截器调用链
        return invocation.proceed();

    }

    public String getPageSql(String sql,PageUtil pageUtil){
        if(dataName.equals("mysql")){
            return sql+" limit "+pageUtil.getStart()+","+pageUtil.getLimit();
        }else if(dataName.equals("oracle")){
            //拼接oracle的分语句
        }

        return sql+" limit "+pageUtil.getStart()+","+pageUtil.getLimit();
    }

    /**
     * 返回一个动态代理对象
     * 这个参数o是目标对象,就是你要拦截的对象,这里指的是StatementHandler
     * @param o
     * @return
     */
    @Override
    public Object plugin(Object o) {

        return Plugin.wrap(o,this);
    }

    /**
     * 会传入xml文件中配置的属性
     * 可以通过xml做一些初始化工作
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {

        dataName = (String) properties.get("dataName");

    }
}

分页工具类

public class PageUtil {

    private int page;

    private  int limit;

    private  int count;

    private  int start;

    public PageUtil(){
    }

    public PageUtil(int page, int limit){
        this.page = page;
        this.limit = limit;
        this.start = (page-1)*limit;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }


    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public static void main(String[] args) {
        Map<String,PageUtil> map = new HashMap<>();
        PageUtil pageUtil = new PageUtil();
        pageUtil.setPage(1);
        map.put("key",pageUtil);
        //调用逻辑修改map
        方法1(map);
        //直接使用原来对象
        System.out.println(pageUtil.getPage());
    }

    public static void  方法1(Map<String,PageUtil> map){
        map.get("key").setPage(2);
    }
}

插件注入

  • xml注入

在这里插入图片描述
还可以配置属性,配置的属性值可以在自定义的插件中拿到
在这里插入图片描述

  • java注入

分页测试

public class MybatisTest {
  public static void main(String[] args) {
    InputStream inputStream = null;
    try {
      inputStream = Resources.getResourceAsStream("mybatis-config.xml");
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
      PageUtil pageUtil = new PageUtil(2, 5);
      HashMap<String, Object> map1 = new HashMap<>();
      map1.put("page",pageUtil);
      List<Map<String, Object>> list = mapper.listPersonByPage(map1);
//      List<Map<String, Object>> list = mapper.listPerson();
      for (Map<String, Object> map : list) {
        System.out.print(map.get("PERSON_ID")+"\t");
        System.out.print(map.get("NAME")+"\t");
        System.out.print(map.get("PERSON_ADDR")+"\t");
        System.out.print(map.get("BIRTHDAY")+"\t");
        System.out.print(map.get("GENDER")+"\t");
        System.out.println();
      }
//      Map<String, Object> map = mapper.selectPerson(1);
//      System.out.println(map.get("PERSON_ID"));
//      System.out.println(map.get("NAME"));
//      System.out.println(map.get("PERSON_ADDR"));
//      System.out.println(map.get("BIRTHDAY"));
//      System.out.println(map.get("GENDER"));
//      PersonMapper mapper1 = sqlSession.getMapper(PersonMapper.class);
//      Map<String, Object> map1 = mapper1.selectPerson(1);

    } catch (IOException e) {
      e.printStackTrace();
    }finally {
      try {
        inputStream.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }
}

猜你喜欢

转载自blog.csdn.net/qq_35262405/article/details/101310045