Mybatis深入(插件原理)

Mybatis 插件

1.1 插件介绍

介绍:

Mybatis作为一个应用广泛的ORM开源框架,其拥有很大的灵活性,在四大组件(Excutor、 StatementHandler、ParameterHandler、ResultHandler)处理了简单易用的插件扩展机制,Mybatis对持 久层的操作就是借助于四大核心对象。Mybatis支持用插件对四大核心对象进行拦截,对Mybatis来说插 件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的动态代理实现的,换句话 说,Mybatis中的四大对象都是代理对象

Mybatis所允许拦截的方法如下:

执行器Executor(update、query、commit、rollback等方法);
参数处理器ParameterHandler(getParameterObject、setParameters方法);
结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法);

1.2 原理

在四大对象创建的时候,

	1. 每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler);
	2. 获取到的所有interceptor(拦截器){插件需要实现的接口};调用interceptor.plugin(target);
返回target包装后的对象;
	3. 插件机制,我们可以使用插件为目标对象;AOP(面向切面)我们的插件可以为四大对象创建出代理对象
,代理对象就可以拦截到四大对象的每一个执行;

简单截取Executor具体实现类获取代码:

 public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object executor;
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }

        if (this.cacheEnabled) {  // 二级缓存Executor
            executor = new CachingExecutor((Executor)executor);
        }
		// 每个Executor实例,都会被经过interceptorChain进行处理
        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

拦截原理参考设计模式 JDK动态代理+责任链设计模式

在实际开发过程中,我们经常使用的Mybaits插件就是分页插件了,
通过分页插件我们可以在不用写count语句和limit的	情况下就可以获取分页
后的数据,给我们开发带来很大的便利。除了分页,插件使用场景主要还有
更新数据库的通用字段,分库分表,加解密等的处理。

1.3 自定义插件

1.3.1 Mybatis插件接口-interceptor

intercept方法,插件的核心方法
plugin方法,生成target的代理对象
setProperties方法,传递插件所需参数

1.3.2 自定义插件

设计实现一个自定义插件:

package com.lg.plugin;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;

import java.sql.Connection;
import java.util.Properties;

@Intercepts({  // 注意这个大花括号,也就是说这里可以多个@Signature对多个地方进行拦截,都用这个拦截器
    @Signature(type = StatementHandler.class,  // 拦截哪个类
    method = "prepare",  // 拦截哪个方法
    args = {Connection.class,Integer.class})  // 为避免拦截到重载的方法,传入拦截参数,入参必须和拦截方法入参类型一致
})

public class MyPlugin implements Interceptor {
    // 由以上原理可看出,目标对象的目标方法被执行时,每次都会执行 intercept 方法
    public Object intercept(Invocation invocation) throws Throwable {
        System.out.println("对方法进行增强...");
        return invocation.proceed();
    }

    // 将拦截的目标类对象,作为入参,传入拦截器,拦截器进行代理,生成代理对象

    /**
     * 包装目标对象,为目标对象创建代理对象 
     * @param target 为拦截的对象
     * @return 代理对象
     */
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    //获取配置文件的属性
    // 插件初始化的时候调用,也只调用一次,插件配置的属性从这里进来
    public void setProperties(Properties properties) {
        System.out.println("接收到传入的参数为==>" + properties);
    }
}

1.4 Mybatis插件机制-pageHelper

开发步骤:

  1. 导入通用PageHelper的坐标
  2. 在mybatis核心配置文件中配置PageHelper插件
  3. 测试分页数据获取

①导入通用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>

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

<plugins>
        <plugin interceptor="com.github.pagehelper.PageHelper">
            <property name="dialect" value="mysql"/>
        </plugin>
    </plugins>

③ 测试分页代码实现

  // 测试分页
    @Test
    public void testPage(){
        PageHelper.startPage(1,3);
        StuDao mapper = sqlSession.getMapper(StuDao.class);
        List<Stu> stus = mapper.selectList();
        for (Stu stu:
             stus) {
            System.out.println(stu.toString());
        }
        PageInfo<Stu> stuPageInfo = new PageInfo(stus);
        System.out.println("总页码==>" + stuPageInfo.getPages());
        System.out.println("总条数==>" + stuPageInfo.getTotal());
        System.out.println("当前页==>" + stuPageInfo.getPageNum());
    }

撸结果:
在这里插入图片描述

1.5 Mybatis通用插件Mapper

什么是通用Mapper?

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

如何使用?

    1. 导入通用mapper依赖
 <!--通用Mapper-->
        <!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>3.4.5</version>
        </dependency>
    1. 在mabatis核心配置文件中配置 mapper插件
<plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
	<!--指定当前通用mapper接口使用的是哪个-->
   	<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
 </plugin>
    1. 实体类设置主键,设置表名
package com.lg.pojo;

import javax.persistence.*;

/**
 * 学生信息表
 */
@Table(name = "Stu")  // 映射某张表
public class Stu {
    @Override
    public String toString() {
        return "Stu{" +
                "sid=" + sid +
                ", sname='" + sname + '\'' +
                ", gender='" + gender + '\'' +
                ", phone='" + phone + '\'' +
                ", hobby='" + hobby + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
    @Id // 对应的是注解id
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // mysql是 IDENTITY 自增,Oracle是 SEQUENCE 序列
    private int sid;
    @Column(name = "sname")  // 名称一致,则不用写
    private String sname;
    private String gender;
    private String phone;
    private String hobby;
    private String info;

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

④定义通用Mapper

// 只需继承 Mapper 接口即可
public interface StuMapper_Plugin extends Mapper<Stu> {
}

测试类:

// 测试通用Mapper插件
    @Test
    public void testMapper(){
        StuMapper_Plugin mapper = sqlSession.getMapper(StuMapper_Plugin.class);
        Stu stu = new Stu();
        stu.setSid(313);
        Stu stu1 = mapper.selectOne(stu);
        System.out.println(stu1.toString());

        // 2. Example方法
        Example example = new Example(Stu.class);
        example.createCriteria().andEqualTo("sid",313);
        mapper.selectByExample(example);
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值