MyBatis 入门(六)--分页查询(2) -插件方式

一、Mybatis插件介绍

摘自官方文档:很晦涩,简单阅读一下即可
MyBatis 允许你在某一点拦截已映射语句执行的调用。默认情况下,MyBatis 允许使用插件来拦截方法调用:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的详情可以通过查看每个方法的签名来发现 ,而且它们的源代码在 MyBatis 的发行包中有。你应该理解你覆盖方法的行为,假设你所做的要比监视调用要多。 如果你尝试修改或覆盖一个给定的方法, 你可能会打破 MyBatis 的核心。 这是低层次的类和方法,要谨慎使用插件。
使用插件是它们提供的非常简单的力量。

简单实现拦截器接口, 要确定你想拦截的指定签名。
上面的插件将会拦截在 Executor 实例中所有的“update”方法调用,它也是负责低层次 映射语句执行的内部对象。

插件的作用就是在执行语句前,后都可以自定义需要处理的程序(通过责任链模式 ) ,也就是说可以定义多个需要执行的程序。

二、 实现一个自己的拦截器

  • 继承Interceptor
package com.elements.user.plugins;

import java.sql.Connection;
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.Signature; 

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

    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("bbbbbbbbbb");

        return null;
    }

    public Object plugin(Object target) {
        System.out.println("target is :"+target.getClass().getName());
        return target;

    }

    public void setProperties(Properties properties) {
        System.out.println(properties.toString());

    }

}
  • mybatis-config.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <properties resource="jdbc.properties" />

   <!-- 自定义分页拦截器 -->
    <plugins>
        <plugin interceptor="com.elements.user.plugins.PageInterceptor">
            <property name="someProperty" value="100" />
        </plugin>
    </plugins>

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" />
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password}" />
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper class="com.elements.user.dao.UserMapper" />

    </mappers>

</configuration>

测试输出结果

{someProperty=100}
target is :org.apache.ibatis.executor.CachingExecutor
target is :org.apache.ibatis.scripting.defaults.DefaultParameterHandler
target is :org.apache.ibatis.executor.resultset.DefaultResultSetHandler
target is :org.apache.ibatis.executor.statement.RoutingStatementHandler

intercept没有被调用
第一步 我们的属性,从配置文件里面获取了,这里是 someProperty 值是 someProperty
第二步 我们的plugin ,被调用了4次,分别是 CachingExecutor, DefaultParameterHandler ,DefaultResultSetHandler,RoutingStatementHandler

  1. CachingExecutor :二级缓存执行器 实现 Executor
  2. DefaultParameterHandler :参数处理 实现ParameterHandler
  3. DefaultResultSetHandler :实现 ResultSetHandler
      ResultSetHandler只负责两件事:
       (1).处理Statement执行后产生的结果集,生成结果列表
       (2).处理存储过程执行后的输出参数
  4. RoutingStatementHandler:实现了StatementHandler

三、分页实现

  • 分页基类同分页一模式(代码略有不同)
package com.elements.user.plugins;

public class Page {

    // 定义分页的默认大小
    private int OnePageSize = 5;

    // 当前页数
    private int nowPage = 1;

    public int getOnePageSize() {
        return OnePageSize;
    }

    public void setOnePageSize(int onePageSize) {
        OnePageSize = onePageSize;
    }

    public int getStartRow() {
        return startRow;
    }

    public void setStartRow(int startRow) {
        this.startRow = startRow;
    }

    public int getEndRow() {
        return endRow;
    }

    public void setEndRow(int endRow) {
        this.endRow = endRow;
    }

    // 开始的行
    private int startRow = 0;
    {
        startRow = (nowPage - 1) * OnePageSize;
    }

    // 结束的行
    private int endRow = 0;
    {
        endRow = nowPage * OnePageSize;
    }

    // 总的页数
    private int AllPages = 0;

    // 总记录数
    private int allRows = 0;

    public int getAllRows() {
        return allRows;
    }

    public void setAllRows(int allRows) {
        this.allRows = allRows;
        this.AllPages = allRows % OnePageSize == 0 ? (int) (allRows / OnePageSize)
                : (int) (allRows / OnePageSize) + 1;
    }

    public int getNowPage() {
        return nowPage;
    }

    public void setNowPage(int nowPage) {
        this.nowPage = nowPage;
        startRow = (nowPage - 1) * OnePageSize;
        endRow = nowPage * OnePageSize;
    }
}
  • 继承page的User类同分页一,不再展示代码
  • UserMapper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.elements.user.dao.UserMapper" >


  <resultMap id="BaseResultMap" type="com.elements.user.model.User" >
    <id column="userId" property="userid" jdbcType="INTEGER" />
    <result column="UserName" property="username" jdbcType="VARCHAR" />
    <result column="UserEmail" property="useremail" jdbcType="VARCHAR" />
    <result column="createTime" property="createtime" jdbcType="VARCHAR" />
    <result column="updateTime" property="updatetime" jdbcType="VARCHAR" />
  </resultMap>


  <sql id="Base_Column_List" >
    userId, UserName, UserEmail, createTime, updateTime
  </sql>

  <select id="selectUserPage" parameterType="com.elements.user.model.User" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from user 
     where 1=1 
     and UserName like '%%'
  </select>

</mapper>

最重要的插件类PageInterceptor

package com.elements.user.plugins;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
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.scripting.defaults.DefaultParameterHandler;

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

    public Object plugin(Object target) {
        System.out.println("target is :" + target.getClass().getName());
        if (target instanceof RoutingStatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    public void setProperties(Properties properties) {
        System.out.println(properties.toString());

    }

    public Object intercept(Invocation invocation) throws Throwable {

        if (invocation.getTarget() instanceof RoutingStatementHandler) {

            RoutingStatementHandler statementHandler = (RoutingStatementHandler) invocation
                    .getTarget();
            StatementHandler delegate = (StatementHandler) ReflectHelper
                    .getFieldValue(statementHandler, "delegate");
            BoundSql boundSql = delegate.getBoundSql();
            Object obj = boundSql.getParameterObject();

            if (obj instanceof Page) {

                Page page = (Page) obj;
                // 通过反射获取delegate父类BaseStatementHandler的mappedStatement属性
                MappedStatement mappedStatement = (MappedStatement) ReflectHelper
                        .getFieldValue(delegate, "mappedStatement");
                // 拦截到的prepare方法参数是一个Connection对象
                Connection connection = (Connection) invocation.getArgs()[0];

                // 获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
                String sql = boundSql.getSql();
                // 给当前的page参数对象设置总记录数
                this.setTotalRecord(page, mappedStatement, connection);
                // 获取分页Sql语句
                String pageSql = this.getPageSql(page, sql);
                // 利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
                ReflectHelper.setFieldValue(boundSql, "sql", pageSql);
            }
        }

        return invocation.proceed();

    }

    private String getPageSql(Page page, String sql) {

        StringBuffer sqlBuffer = new StringBuffer(sql);

        sqlBuffer.insert(0, "select * from (").append(")temp where 1=1 ")
                .append(" limit ").append(page.getStartRow()).append(",")
                .append(page.getOnePageSize());

        return sqlBuffer.toString();
    }

    private void setTotalRecord(Page page, MappedStatement mappedStatement,
            Connection connection) {

        // delegate里面的boundSql也是通过mappedStatement.getBoundSql(paramObj)方法获取到的。
        BoundSql boundSql = mappedStatement.getBoundSql(page);
        // 获取到我们自己写在Mapper映射语句中对应的Sql语句
        String sql = boundSql.getSql();
        // 通过查询Sql语句获取到对应的计算总记录数的sql语句
        String countSql = this.getCountSql(sql);

        // 通过BoundSql获取对应的参数映射
        List<ParameterMapping> parameterMappings = boundSql
                .getParameterMappings();
        // 利用Configuration、查询记录数的Sql语句countSql、参数映射关系parameterMappings和参数对象page建立查询记录数对应的BoundSql对象。
        BoundSql countBoundSql = new BoundSql(
                mappedStatement.getConfiguration(), countSql,
                parameterMappings, page);
        // 通过mappedStatement、参数对象page和BoundSql对象countBoundSql建立一个用于设定参数的ParameterHandler对象
        ParameterHandler parameterHandler = new DefaultParameterHandler(
                mappedStatement, page, countBoundSql);
        // 通过connection建立一个countSql对应的PreparedStatement对象。
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = connection.prepareStatement(countSql);
            // 通过parameterHandler给PreparedStatement对象设置参数
            parameterHandler.setParameters(pstmt);
            // 之后就是执行获取总记录数的Sql语句和获取结果了。
            rs = pstmt.executeQuery();
            if (rs.next()) {
                int totalRecord = rs.getInt(1);
                // 给当前的参数page对象设置总记录数
                page.setAllRows(totalRecord);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null)
                    rs.close();
                if (pstmt != null)
                    pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

    private String getCountSql(String sql) {
        StringBuffer sqlBuffer = new StringBuffer(sql);
        sqlBuffer.insert(0, "select count(*) from (").append(
                ")temp where 1=1 ");
        return sqlBuffer.toString();
    }

}
  • 测试类
package com.elements.user;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

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 org.junit.Test;
import com.elements.user.dao.UserMapper;
import com.elements.user.model.User;

public class TestUserPage {

    @Test
    public void TestUserPageSelect() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(inputStream);

        SqlSession session = sqlSessionFactory.openSession();
        try {


            UserMapper user = (UserMapper) session
                    .getMapper(UserMapper.class);

            User u=new User();

            List<User> list =user.selectUserPage(u);

            System.out.print(list);

        } finally {
            session.close();
        }

    }

}

四、总结

只要你的查询参数继承Page类,就会自动分页,查询总记录,总页数(在page内 )。 

项目代码地址: http://pan.baidu.com/s/1sld2aZV

本文章参考了:
(1)http://blog.csdn.net/hfmbook/article/details/41985853

你可能感兴趣的:(mybatis,分页,插件)