Mybatis从入门到精通——插件和拦截器的使用(19)

一、Mybatis插件和拦截器的使用

在Mybatis中,插件是通过实现拦截器接口进行拦截操作而完成的,也就是说插件是基于Mybatis拦截器的,Mybatis中拦截器的接口为Interceptor,该拦截器能够对Mybatis内部运行的四大接口的方法进行拦截,四大接口分别为:Executor、StatementHandler、ResultSetHandler、ParameterHandler。 

 

二、Executor、StatementHandler、ResultSetHandler、ParameterHandler简介

Mybatis的拦截器通过动态代理,能够拦截Executor、StatementHandler、ResultSetHandler、ParameterHandler四大接口中的一个或多个的任意方法(一般只拦截单个接口的一类方法)。

Executor:

      Mybatis的执行器,主要用于处理缓存、事务管理、获取连接、运行调度、协调另外三个处理器工作。

StatementHandler:

     Mybatis的Statement处理器,主要用于创建Statement、调用Statement执行sql。

ParameterHandler:

    Mybatis的参数处理器,主要用于处理接口方法中参数的转换和最后赋值到Statement上。

ResultSetHandler:

    Mybatis的返回结果集处理器,主要用于处理返回结果集。

 

三、案例

案例说明:

本文写一个简单Executor拦截器进行分页操作,因为插件很少个人开发,本文不影响Mybatis的使用。

User实体类:

public class User {
    private Integer id;

    private String userName;

    private String realName;

    private String sex;

    private String mobile;

    private String email;

    private String note;

    private Integer positionId;
}

PageHelper(分页助手)实体类:

public class PageHelper {

  private static ThreadLocal<PageInfo> PAGE_CONTEXT = new ThreadLocal();

  public static PageInfo startPage(int pageNum, int pageSize) {
    PageInfo pageInfo = new PageInfo(pageNum,pageSize);
    int startRow = (pageNum - 1) * pageSize;
    int endRow = pageNum * pageSize;
    pageInfo.setStartRow(startRow);
    pageInfo.setEndRow(endRow);
    putPageInfo(pageInfo);
    return pageInfo;
  }

  public static PageInfo getPageInfo() {
    return PAGE_CONTEXT.get();
  }

  public static void putPageInfo(PageInfo pageInfo) {
    PAGE_CONTEXT.set(pageInfo);
  }

  public static void clearPageInfo() {
    PAGE_CONTEXT.remove();
  }

}

PageInfo(分页信息)实体类:

public class PageInfo {

  /**
   * 页码,从1开始
   */
  private int pageNum;
  /**
   * 页面大小
   */
  private int pageSize;
  /**
   * 起始行
   */
  private int startRow;
  /**
   * 末行
   */
  private int endRow;
  /**
   * 总数
   */
  private long total;

  public PageInfo(int pageNum, int pageSize) {
    this.pageNum = pageNum;
    this.pageSize = pageSize;
  }
}
PageInterceptor(分页拦截器)类:
@Intercepts(
  {
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
  }
)
public class PageInterceptor implements Interceptor {

  /**
   * 执行拦截逻辑的方法
   *
   * @param invocation 代理对象的封装
   * @return 返回值
   * @throws Throwable
   */
  @Override
  public Object intercept(Invocation invocation) throws Throwable {
    try {
      Object[] args = invocation.getArgs();
      MappedStatement mappedStatement = (MappedStatement) args[0];
      Object parameter = args[1];
      RowBounds rowBounds = (RowBounds) args[2];
      ResultHandler resultHandler = (ResultHandler) args[3];
      Executor executor = (Executor) invocation.getTarget();
      BoundSql boundSql;
      CacheKey cacheKey;
      //4个参数时
      if (args.length == 4) {
        boundSql = mappedStatement.getBoundSql(parameter);
        //只能按照原来的逻辑创建一个cacheKey
        cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSql);
      } else {
        //6 个参数时
        cacheKey = (CacheKey) args[4];
        boundSql = (BoundSql) args[5];
      }
      String sql = boundSql.getSql();
      //判断是否需要分页
      PageInfo pageInfo = PageHelper.getPageInfo();
      if (pageInfo == null) {
        return invocation.proceed();
      }
      String pageSql = sql + " limit " + pageInfo.getStartRow() + "," + pageInfo.getEndRow();
      BoundSql pageBoundSql = new BoundSql(mappedStatement.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter);
      List<Object> list = executor.query(mappedStatement, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, pageBoundSql);
      return list;
    } finally {
      //清理
      PageHelper.clearPageInfo();
    }
  }

  /**
   * 给被拦截的对象生成一个代理对象
   *
   * @param target 目标对象
   * @return 代理对象
   */
  @Override
  public Object plugin(Object target) {
    //调用Mybatis提供的即可
    return Plugin.wrap(target, this);
  }

  /**
   * 获取插件配置的信息
   *
   * @param properties 配置属性信息
   */
  @Override
  public void setProperties(Properties properties) {
    System.out.println(properties.getProperty("name"));
  }
}

mapper映射文件:

<?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.my.mapper.UserMapper">

  <resultMap id="BaseResultMap" type="com.my.entity.User">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="user_name" jdbcType="VARCHAR" property="userName" />
    <result column="real_name" jdbcType="VARCHAR" property="realName" />
    <result column="sex" jdbcType="CHAR" property="sex" />
    <result column="mobile" jdbcType="VARCHAR" property="mobile" />
    <result column="email" jdbcType="VARCHAR" property="email" />
    <result column="note" jdbcType="VARCHAR" property="note" />
    <result column="position_id" jdbcType="INTEGER" property="positionId" />
  </resultMap>

  <sql id="Base_Column_List">
    id, user_name, real_name, sex, mobile, email, note, position_id
  </sql>

  <select id="selectList" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user
  </select>
</mapper>

测试代码:

public class MybatisTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void init() throws IOException {
        String resource = "mybatis-config.xml";
        //1.使用mybatis的工具读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //2.创建sqlSessionFactory
        sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
        inputStream.close();
    }

    /**
     * 测试Interceptor
     */
    @Test
    public void testInterceptor() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        //分页
        PageHelper.startPage(1,2);
        List<User> users = userMapper.selectList();
        System.out.println(users);
        sqlSession.close();
    }

}

四、使用步骤

步骤:

1.实现Interceptor接口,重写3个方法。

2.在Mybatis配置中注册该拦截器,通过plugins下的plugin标签,并设置初始化参数。

3.在其Interceptor接口实现类上使用@Intercepts和@Signature注解标明拦截的接口和方法。

 

Interceptor接口方法:

1.void setProperties(Properties properties):

    该方法为获取在plugin标签下设置的初始化值,会传递到该方法中。

2.Object plugin(Object target):

    返回代理对象,此方法中调用默认的操作即可,Plugin.wrap(target, this),该静态方法会返回正确的代理对象。

3.Object intercept(Invocation invocation):

    对拦截的方法进行增强和具体操作。

@Intercepts说明:

 该注解标记在拦截器实现类的类上,用来表明需要拦截的类和拦截的方法,具体设置是通过@Signature完成的。

@Signature说明:

该注解标记拦截的类,以及拦截的方法,只能拦截上文所述的四个接口的方法。

 

五、补充说明

1.本文内容不适合新手学习,但可以看一遍,留个印象。

2.拦截的四大接口方法过于繁多,只能自行通过IDE搜索查看。

3.对Mybatis内部运行原理不熟悉的请不要滥用该拦截器。

        

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值