Mybatis插件学习与实践(含具体代码)
来源:互联网 发布:穿越火线网络连接异常 编辑:程序博客网 时间:2024/05/17 18:02
首先,先说说什么是插件(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);}// 取出即将执行的sqlString 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;// 重写要执行的sqlmetaStmtHandler.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 configurationPUBLIC "-//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)需要了解四大对象及其方法的作用,准确判断需要拦截什么对象,什么方法,参数是什么,才能确定签名如何编写。
- Mybatis插件学习与实践(含具体代码)
- MyBatis学习总结(15)——定制Mybatis自动代码生成的maven插件
- Spring MVC+MyBatis+MySql 具体实践
- 【机器学习PAI实践十二】机器学习实现男女声音识别分类(含语音特征提取数据和代码)
- 集合遍历与迭代器(含代码)
- DataBinding与MVVM(含代码)
- DataBinding与MVVM(含代码)
- linux下的僵尸进程产生原因和解决方法(含具体代码)
- matlab与机器学习(一)基础知识(含代码解析)
- matlab与机器学习(二)程序基本操作(含代码解析)
- openSSL学习例子(代码含c与java两版本)
- Spring+SpringMVC+Mybatis整合开发实践(含源码(两个整合实例)、PPT)
- Spring+SpringMVC+MyBatis整合开发实践(含源码(两个整合实例)、PPT)
- MyBatis的插件安装与自动生成代码
- 攻防具体实践(1)
- MyBatis基于Spring-boot集成通用Mapper以及pagehelper分页插件(含源码下载)
- mybatis 插件 generator 学习
- SpringBoot入门学习(含代码)
- linux 学习 3-4
- 你需要理解的Java反射机制知识总结
- java类初始化顺序探索
- 第一章 机器学习基础
- java 车牌号正则校验
- Mybatis插件学习与实践(含具体代码)
- 51Nod-1515-明辨是非
- 程序员面试宝典试题总结
- 应用启动图标未读消息数显示 工具类
- I3C 协议的介绍以及 在sensor 传感器中的应用和实战
- Android常用开源项目(四十二)
- [Leetcode] 346. Moving Average from Data Stream 解题报告
- OV7670_SDRAM_LCD视频显示系统框架
- ionic3+angular4+cordova 项目实例