MyBatis(九)插件

本文详细介绍了MyBatis插件的开发过程,包括如何使用Interceptor接口实现拦截StatementHandler方法,动态改变SQL参数,以及插件在执行流程中的作用。通过实例演示了插件如何层层包裹对象并影响查询结果。
摘要由CSDN通过智能技术生成

插件

  • MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。
  • MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。
  • 默认情况下,MyBatis 允许使用插件来拦截的方法调用
    包括:
    • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    • ParameterHandler (getParameterObject, setParameters)
    • ResultSetHandler (handleResultSets, handleOutputParameters)
    • StatementHandler (prepare, parameterize, batch, update, query)

插件开发

• 插件开发步骤
1)、编写插件类实现Interceptor接口,并使用@Intercepts注解完成插件签名

package com.xiaoqiu.dao;

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

import java.util.Properties;

/**
 * @author 小邱
 * @version 0.0.1
 * @description MyFirstPlugin
 * @since 2021/10/18 19:31
 */
//完成插件签名:告诉MyBatis当前插件用来拦截哪个对象的哪个方法
@Intercepts({@Signature(type= StatementHandler.class,method="parameterize",args=java.sql.Statement.class)})
public class MyFirstPlugin implements Interceptor {


    //intercept:拦截:拦截目标对象的目标方法的执行
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());

        //执行目标方法
        Object proceed = invocation.proceed();
        //返回执行后的返回值
        return proceed;
    }

    //plugin:包装目标对象的:包装:为目标对象创建一个代理对象
    @Override
    public Object plugin(Object target) {

        System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);

        //我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
        Object wrap = Plugin.wrap(target, this);
        //返回为当前target创建的动态代理
        return wrap;
    }

    //setProperties:将插件注册时 的property属性设置进来
    @Override
    public void setProperties(Properties properties) {

        System.out.println("插件配置的信息:"+properties);

    }
}

2)、在全局配置文件中注册插件

<plugins>
	<plugin interceptor="com.xiaoqiu.dao.MyFirstPlugin">
		<property name="username" value="root"/>
		<property name="password" value="root"/>
	</plugin>
</plugins>

3)、测试

public class MyBatisTest {
    public static SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    public static void main(String[] args) {
        try {
            EmployeeMapper mapper = getSqlSessionFactory().openSession().getMapper(EmployeeMapper.class);
            Employee emp = mapper.getEmpById(10);
            System.out.println(emp);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

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

Interceptor接口

• Intercept:拦截目标方法执行
• plugin:生成动态代理对象,可以使用MyBatis提供的Plugin类的wrap方法
• setProperties:注入插件配置时设置的属性

插件原理

在四大对象创建的时候
1)、按照插件注解声明,按照插件配置顺序调用插件plugin方法,每个创建出来的对象不是直接返回的,而是生成被拦截对象的动态代理。
interceptorChain.pluginAll(parameterHandler);
2)、多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链
3)、获取到所有的Interceptor(拦截器)(插件需要实现的接口),目标方法执行时依次从外到内执行调用interceptor.plugin(target)。返回target包装后的对象。
4)、多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值
5)、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)

多个插件执行

添加第二个插件

package com.xiaoqiu.dao;

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.Plugin;
import org.apache.ibatis.plugin.Signature;

@Intercepts({@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)})
public class MySecondPlugin implements Interceptor{

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		System.out.println("MySecondPlugin...intercept:"+invocation.getMethod());
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {

		System.out.println("MySecondPlugin...plugin:"+target);
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		System.out.println("MySecondPlugin--properties");
	}

}

<plugin interceptor="com.xiaoqiu.dao.MySecondPlugin"></plugin>

测试代码不变,运行结果:

插件配置的信息:{password=root, username=root}
MySecondPlugin--properties
MyFirstPlugin...plugin:mybatis将要包装的对象org.apache.ibatis.executor.CachingExecutor@396a51ab
MySecondPlugin...plugin:org.apache.ibatis.executor.CachingExecutor@396a51ab
MyFirstPlugin...plugin:mybatis将要包装的对象org.apache.ibatis.scripting.defaults.DefaultParameterHandler@3e6fa38a
MySecondPlugin...plugin:org.apache.ibatis.scripting.defaults.DefaultParameterHandler@3e6fa38a
MyFirstPlugin...plugin:mybatis将要包装的对象org.apache.ibatis.executor.resultset.DefaultResultSetHandler@1bce4f0a
MySecondPlugin...plugin:org.apache.ibatis.executor.resultset.DefaultResultSetHandler@1bce4f0a
MyFirstPlugin...plugin:mybatis将要包装的对象org.apache.ibatis.executor.statement.RoutingStatementHandler@5c3bd550
MySecondPlugin...plugin:org.apache.ibatis.executor.statement.RoutingStatementHandler@5c3bd550
DEBUG 10-18 20:09:11,007 ==>  Preparing: select id, last_name lastName, email, gender from tbl_employee where id = ?   (BaseJdbcLogger.java:145) 
MySecondPlugin...intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
MyFirstPlugin...intercept:public abstract void org.apache.ibatis.executor.statement.StatementHandler.parameterize(java.sql.Statement) throws java.sql.SQLException
DEBUG 10-18 20:09:11,069 ==> Parameters: 10(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 10-18 20:09:11,118 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee [id=10, lastName=李四, email=lisi@qq.com, gender=1]

Process finished with exit code 0

在这里插入图片描述
案例:动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工

package com.xiaoqiu.dao;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.util.Properties;

@Intercepts({@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)})
public class MyThirdPlugin implements Interceptor{

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
		Object target = invocation.getTarget();
		System.out.println("当前拦截到的对象:"+target);
		//拿到:StatementHandler==>ParameterHandler===>parameterObject
		//拿到target的元数据
		MetaObject metaObject = SystemMetaObject.forObject(target);
		Object value = metaObject.getValue("parameterHandler.parameterObject");
		System.out.println("sql语句用的参数是:"+value);
		//修改完sql语句要用的参数
		metaObject.setValue("parameterHandler.parameterObject", 3);
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

}

<plugin interceptor="com.xiaoqiu.dao.MyThirdPlugin"></plugin>

测试代码

public class MyBatisTest {
    public static SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    public static void main(String[] args) {
        try {
            EmployeeMapper mapper = getSqlSessionFactory().openSession().getMapper(EmployeeMapper.class);
            Employee emp = mapper.getEmpById(1);
            System.out.println(emp);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

运行结果:

DEBUG 10-18 20:27:58,037 ==>  Preparing: select id, last_name lastName, email, gender from tbl_employee where id = ?   (BaseJdbcLogger.java:145) 
当前拦截到的对象:org.apache.ibatis.executor.statement.RoutingStatementHandler@23348b5d
sql语句用的参数是:1
DEBUG 10-18 20:27:58,192 ==> Parameters: 3(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 10-18 20:27:58,246 <==      Total: 1  (BaseJdbcLogger.java:145) 
Employee [id=3, lastName=jerry, email=jerry@qq.com, gender=0]

Process finished with exit code 0

运行原理:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值