Mybatis插件学习与实践(含具体代码)

首先,先说说什么是插件(plugin),插件类似于spring里面的

拦截器,可以让你在mybatis生命周期某一阶段插入或修改相应逻辑代码。

可以和maven的插件做类似的比较理解。


在说说mybatis的生命周期,在mybatis里面,主要是对以mapper为为中心的生命周期拦截。


在Mapper执行过程中有四大对象,mybatis可以对他们进行拦截。分别是:

      #Executor:是执行sql的全过程,包括组装参数,组装结果集返回和执行sql过程,

都可以拦截。

      #StatementHandler:是执行sql的过程,我们可以重写执行sql的过程。

      #ParameterHandler:是主要拦截执行sql的参数的组装,你可以重写组装参数规则。

      #ResultSetHandler:用于拦截执行结果的组装。


在插件中,可以针对上述4个类进行拦截,可以进一步拦截类中所有方法。

首先要实现Interceptor接口,这里介绍一下Interceptor接口:

      #intercept方法:它将直接覆盖你所拦截对象原有的方法,因此它是插件的核心方法。

      #plugin方法:target被拦截对象,它的作用是给被拦截对象生成一个代理对象,并返回它。

      #setProperties方法:允许再plugin元素中配置所需参数,方法在插件初始化的时候就被调用一次,

然后把插件对象存入配置中。


再一个,需要了解插件实现的大体思路,插件用的是代理模式

具体关于职责链模式,可以参考我的文章:职责联模式-解决不了的事往后传 

还有是代理模式,可以参见我的这两篇,关于jdk代理以及cglib代理

cglib动态代理

jdk动态代理


在mybatis插件设计中,一般会用到一个工具类:MetaObject

它可以有效的读取或者修改一些重要对象的属性,具体是利用反射得到的。

它有3个方法常常被我们用到

      #SystemMetaObject.forObject(Object obj):用于包装对象MetaObject

      #Objetc getValue(String name):用于获取对象属性值,支持OGNL。

      #void setValue(String name,Object value):用于修改duiiang属性值,支持OGNL。

下面给出实例代码:

代码目的是拦截statementHandler,往sql语句中添加一个limit +num字串。

QueryLimitPlugin类:

package study.plugin;

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

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.javassist.tools.reflect.Metaobject;
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.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

@Intercepts({ @Signature(type = StatementHandler.class, // 确定要拦截的对象
		method = "prepare", // 要拦截的方法
		args = { Connection.class, Integer.class }) }) // 拦截方法的参数
public class QueryLimitPlugin implements Interceptor {

	/**
	 * 默认限制查询返回行数
	 */
	private int limit;

	private String dbType;

	public Object intercept(Invocation invocation) throws Throwable {
		// 取出被拦截的对象
		StatementHandler stmtHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStmtHandler = SystemMetaObject.forObject(stmtHandler);
		// 分离代理对象,从而形成多次代理,通过两次循环最原始的被代理类,Mybatis使用的是JDK代理
		while (metaStmtHandler.hasGetter("h")) {
			Object object = metaStmtHandler.getValue("h");
			metaStmtHandler = SystemMetaObject.forObject(object);
		}

		// 分离最后一个代理目标类
		while (metaStmtHandler.hasGetter("target")) {
			Object object = metaStmtHandler.getValue("target");
			metaStmtHandler = SystemMetaObject.forObject(object);
		}
		// 取出即将执行的sql
		String sql = (String) metaStmtHandler.getValue("delegate.boundSql.sql");
		String limitSql;
		// 判断参数是不是mysql数据库且sql有没有被插件重写过,重写过就不重写了
		if ("mysql".equals(this.dbType)) {
			// 去掉前后空格
			sql = sql.trim();
			// 将参数写入到sql
			// limitSql = "select * from ("+sql+")"+LMT_TABLE_NAME + "limit "+limit;
			limitSql = sql + " limit " + limit;
			// 重写要执行的sql
			metaStmtHandler.setValue("delegate.boundSql.sql", limitSql);
		}
		// 调用原来对象的方法,进入责任链的下一层级
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		// 使用mybatis提供的默认的类生成代理对象
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
		String strLimit = (String) properties.getProperty("limit", "50");
		this.limit = Integer.parseInt(strLimit);
		// 这里设置要读取的数据库类型
		this.dbType = (String) properties.getProperty("dbtype", "mysql");
	}

}


接着再在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>
	<typeAliases>
		<typeAlias type="study.helloworld.po.Role" alias="role"/>
	</typeAliases>
	
	<plugins>
		<plugin interceptor="study.plugin.QueryLimitPlugin">
			<property name="dbtype" value="mysql"/>
			<property name="limit" value="50"/>
		</plugin>
	</plugins>
	
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC">
				<property name="autoCommit" value="false"/>
			</transactionManager>
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
				<property name="username" value="root"/>
				<property name="password" value="123456"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- resource="study/helloworld/mapper/roleMapper.xml"  -->
	<mappers>
		<mapper class="study.helloworld.mapper.RoleMapper"/>
	</mappers>
</configuration>

注意要把plugins子标签放在environments前面,否则会报错。


这样以来,每当执行sql语句时,都会插入limit + num再原有sql语句后


这里有几个mybatis插件的设计注意:

1):能不用插件尽量不要用插件,因为它将修改mybatis的底层设计。

2):插件生层是层层代理对象的职责链模式,通过反射方法运行,性能不高,所以减少插件就能减少代理,从而提高

性能。

3)需要了解四大对象及其方法的作用,准确判断需要拦截什么对象,什么方法,参数是什么,才能确定签名如何编写。








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值