mybatis源码学习之插件

mybatis源码学习之插件

  • mybatis插件
    • mybatis插件介绍
    • mybatis插件原理
    • 自定义插件
    • pageHelper
    • 通用mapper插件
  • 学习收获

mybatis插件

mybatis为我们提供了插件,并提供了其为可扩展

可以增强我们使用的灵活性,可以结合项目需求对框架进行拓展

目前我们可以基于mybatis插件实现分页、分表、监控等功能

mybatis插件介绍

mybatis有四大组件

  • Executor(执行器,负责一些增删改查)
  • StatementHandler(sql语法构建器,负责sql预编译)
  • paramterHandler(参数处理器)
  • ResultSetHandler(结果集处理器)

mybatis对持久层的操作就是借助着四大核心对象,我们可以对着四大对象进行拦截加强处理

也就相当于四大组件的拦截器,增强功能的本质上是底层的动态代理实现的

也就是说mybatis的四大组件核心对象就是代理对象

mybatis允许我们拦截的方法如下:

  • Executor: update、query、commit、rollback等
  • StatementHandler: prepare、paramterize、batch、update、query等
  • paramterHandler: getParameterObject、setParamter
  • ResultSetHandler: handleResultSets、handleOutputParamters等

mybatis插件原理

在四⼤核心对象创建的时候
1、每个创建出来的对象不是直接返回的,⽽是interceptorChain.pluginAll(parameterHandler)处理后的
2、获取到所有的Interceptor (拦截器),调⽤ interceptor.plugin(target);返回 target 包装后的对象
3、插件机制,AOP (⾯向切⾯)我们的插件可以为四⼤对象创建出代理对象,代理对象就可以拦截到四⼤对象的每⼀个执⾏;

public ParameterHandler newParameterHandler(MappedStatement mappedStatement,
Object object, BoundSql sql, InterceptorChain interceptorChain){
     
    // 此处已经创建好了ParameterHandler对象
    ParameterHandler parameterHandler =
    mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
    // 此处interceptorChain.pluginAll(parameterHandler)处理 生成代理对象
    parameterHandler = (ParameterHandler)interceptorChain.pluginAll(parameterHandler);
    // 返回的也就是代理对象
    return parameterHandler;
}
public Object pluginAll(Object target) {
     
    // 获取我们的所有拦截器
    for (Interceptor interceptor : interceptors) {
     
        // 参数传入的target就是ParameterHandler的原生对象
        // plugin对原生对象进行处理  就是为当前的原生对象生成一个代理对象
        target = interceptor.plugin(target);
    }
    return target;
}

如果我们要做一个拦截器应该怎么做呢
假设我们要拦截Executor的query方法

首先要创建一个拦截器 这个拦截器要实现Interceptor接口

// 首先加入Intercepts注解代表这是一个拦截器
@Intercepts({
     
    // 这个拦截器具体拦截了什么
    @Signature(
        // 要拦截的类
        type = Executor.class,
        // 要拦截的方法名
        method = "query",
        // 因为会有方法重载 需要标识拦截方法的参数
        args={
     MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})
})
public class ExecutorPlugin implements Interceptor {
     
    // 具体拦截实现
}

然后需要将我们的ExecutorPlugin拦截器配置到核心配置文件中

<plugins>
    <plugin interceptor="com.hen84.plugin.ExecuPlugin">
    </plugin>
</plugins>

这样在我们启动时就可以加载到我们自定义的插件,并保存插件实例到拦截器链中,在我们执行sql时 Executor实例被创建后 mybatis就会为其生成动态代理类,这样在执行query方法前就会调用我们的拦截器里的加强逻辑

接下来我们手动演示一下

自定义插件

package com.hen84.plugin;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.Properties;

@Intercepts({
     
        // 这个拦截器具体拦截了什么
        @Signature(
                // 要拦截的类
                type = Executor.class,
                // 要拦截的方法名
                method = "query",
                // 因为会有方法重载 需要标识拦截方法的参数
                args={
     MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class ExecutorPlugin implements Interceptor {
     

    // 具体的加强操作 只要被拦截的query方法执行时,就会先调用该方法
    public Object intercept(Invocation invocation) throws Throwable {
     
        System.out.println("开始增强.........");
        return invocation.proceed();//意思就是让原方法执行
    }

    // 把当前拦截器放到拦截器链中
    public Object plugin(Object o) {
     
        System.out.println("把当前拦截器放到拦截器链中"+o);
        Object wrap = Plugin.wrap(o, this);
        return wrap;
    }

    // 配置文件的参数 
    public void setProperties(Properties properties) {
     
        System.out.println("properties" + properties);
    }
}
<plugins>
    <plugin interceptor="com.hen84.plugin.ExecutorPlugin">
        <property name="测试" value="123456"/>
    </plugin>
</plugins>

这样每当我们执行sql语句之前都会先执行我们的加强方法

pageHelper

pageHelper是mybatis为我们提供的分页插件,那么他怎样使用呢

导⼊PageHelper坐标

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>3.7.5</version>
</dependency>
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>0.9.1</version>
</dependency>

在核心配置文件中配置PageHelper插件

<plugin interceptor="com.github.pagehelper.PageHelper">
    <!—指定⽅⾔ —>
    <property name="dialect" value="mysql"/>
</plugin>

附上pageHelper的部分代码

// 能够看到和我们的配置方法是一样的
// 也就是在每次执行Executor的query方法前都会调用intercept加强方法
@Intercepts({
     @Signature(
    type = Executor.class,
    method = "query",
    args = {
     MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class PageHelper implements Interceptor {
     
    
}

演示

InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
// 分页查询   startPage(要查询第几页,一页有多少条数据)
PageHelper.startPage(1,1);
List<User> users = userMapper1.selectList();

控制台打印

// PageHelper为我们做了什么事
// 1 先查询总条数
17:44:18,598 DEBUG selectList_PageHelper_Count:159 - ==>  Preparing: SELECT count(*) FROM t_user 
17:44:18,912 DEBUG selectList_PageHelper_Count:159 - ==> Parameters: 
17:44:19,127 DEBUG selectList_PageHelper_Count:159 - <==      Total: 1
// 根据我们PageHelper.startPage(1,1);直接改变我们的sql完成分页查询
17:44:19,129 DEBUG selectList_PageHelper:159 - ==>  Preparing: select user_id userId,user_name userName from t_user limit ?,? 
17:44:19,130 DEBUG selectList_PageHelper:159 - ==> Parameters: 0(Integer), 1(Integer)
17:44:19,135 DEBUG selectList_PageHelper:159 - <==      Total: 1

通用mapper插件

通⽤Mapper就是为了解决单表增删改查,基于Mybatis的插件机制。开发⼈员不需要编写SQL,不需要
在DAO中增加⽅法,只要写好实体类,就能⽀持相应的增删改查⽅法

导入坐标

<dependency>
	<groupId>tk.mybatis</groupId>
	<artifactId>mapper</artifactId>
	<version>3.1.2</version>
</dependency>

在核心配置文件中完成配置

<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
<!-- 通⽤Mapper接⼝,多个通⽤接⼝⽤逗号隔开 -->
    <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
</plugin>

实例演示

// 指定sql表
@Table(name = "t_user")
public class User implements Serializable {
     

    @Id // 指定主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 指定自动增长
    private Long userId;

    @Column(name = "user_name")  //指定sql字段名称
    private String userName;

    @Column(name = "user_phone")  //指定sql字段名称
    private String userPhone;
    
    @Transient // 表明这个不是数据库的字段
    private String noSql;
}
// 实现我们指定的通用mapper接口
// mapper接口中有为我们写好的各种单表查询方法
public interface UserMapper extends Mapper<User> {
     

}

如果这些写好的sql并不能满足mybatis还为我们提供了自定义查询

//example⽅法
Example example = new Example(User.class);
// 设置条件 id = 1
example.createCriteria().andEqualTo("id", 1);
//⾃定义查询
List<User> users = userMapper.selectByExample(example);
}

学习收获

1、mybatis可拦截处理方法都有哪些
2、mybatis自定义插件机制
3、对平常使用的pageHelper和通用Mapper有了更深刻的印象

你可能感兴趣的:(mybatis源码学习,mybatis,源码,plugin,java)