MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

除了用插件来修改 MyBatis 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 SqlSessionFactoryBuilder.build(myConfig) 方法即可。

一、可拦截的接口

1.Executor-执行器

public interface Executor {
// 不需要 ResultHandler
ResultHandler NO_RESULT_HANDLER = null;
// 更新
int update(MappedStatement ms, Object parameter) throws SQLException;
// 查询(先查缓存),带分页,BoundSql
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
// 查询,带分页
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
// 查询游标
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
// 刷新(Statement)批处理语句
List<BatchResult> flushStatements() throws SQLException;
// 提交事务,参数为是否要强制
void commit(boolean required) throws SQLException;
// 回滚事务,参数为是否要强制
void rollback(boolean required) throws SQLException;
// 创建 CacheKey
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
// 判断是否缓存了,通过 CacheKey
boolean isCached(MappedStatement ms, CacheKey key);
// 清理 Session(本地一级) 缓存
void clearLocalCache();
// 延迟加载
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
// 获取事务
Transaction getTransaction();
// 关闭连接
void close(boolean forceRollback);
// 是否关闭
boolean isClosed();
// 设置 Executor
void setExecutorWrapper(Executor executor);
}

2.ParameterHandler-参数处理器

public interface ParameterHandler {
// 获取参数
Object getParameterObject();
// 设置参数
void setParameters(PreparedStatement ps) throws SQLException;
}

3.ResultSetHandler-结果集处理器

public interface ResultSetHandler {
// 将结果集转化成 List
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
// 将结果集转化成 Cursor
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
// 处理存储过程的 OUT(输出) 参数
void handleOutputParameters(CallableStatement cs) throws SQLException;
}

4.StatementHandler-SQL语句处理器

public interface StatementHandler {
// 准备语句
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
// 参数处理
void parameterize(Statement statement) throws SQLException;
// 批量处理
void batch(Statement statement) throws SQLException;
// 更新处理
int update(Statement statement) throws SQLException;
// 查询处理,结果给 ResultHandler
<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
<E> Cursor<E> queryCursor(Statement statement) throws SQLException;
// 获取绑定 SQL 语句
BoundSql getBoundSql();
// 获取参数处理器
ParameterHandler getParameterHandler();
}

二、自定义插件

实现 Interceptor 接口,并指定想要拦截的方法签名。最后在 xml 文件中配置全类名即可。

1.bean

@Alias("myUser")
public class MyUser implements Serializable {
private Integer id;
private String name;
private Integer age;

2.dao接口

public interface MyUserMapperAnnotation {
@Select("select * from myuser")
List<MyUser> selectMyUser(String a);
}

3.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>
<!-- 注意 plugins 在配置文件中的位置
properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers? -->
<plugins>
<plugin interceptor="com.plugins.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins> <environments default="development-mysql">
<environment id="development-mysql">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://192.168.8.136:3306/mybatis?allowMultiQueries=true"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments> <mappers>
<mapper class="com.dao.MyUserMapperAnnotation"/>
</mappers>
</configuration>

4.ExamplePlugin

/**
* @Intercepts 拦截器注解,包括一个或多个 @Signature
* @Signature 拦截的目标类信息,包括 type、method、args
* * type 要拦截的接口类型
* * method 接口中的方法名
* * args 方法的所有入参类型
*/
@Intercepts({
@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class ExamplePlugin implements Interceptor {
/**
* 拦截目标对象的目标方法的执行,将自定义逻辑写在该方法中
*/
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("ExamplePlugin...intercept:" + invocation.getMethod()); // MetaObject 是 Mybatis 提供的一个用于访问对象属性的对象
MetaObject metaObject = SystemMetaObject.forObject(invocation); System.out.println("当前拦截到的对象:" + metaObject.getValue("target"));
System.out.println("SQL语句:" + metaObject.getValue("target.delegate.boundSql.sql"));
System.out.println("SQL语句入参:" + metaObject.getValue("target.delegate.parameterHandler.parameterObject"));
System.out.println("SQL语句类型:" + metaObject.getValue("target.delegate.parameterHandler.mappedStatement.sqlCommandType"));
System.out.println("Mapper方法全路径名:" + metaObject.getValue("target.delegate.parameterHandler.mappedStatement.id")); // 修改 SQL 语句
String newSQL = metaObject.getValue("target.delegate.boundSql.sql") + " limit 2";
metaObject.setValue("target.delegate.boundSql.sql", newSQL);
System.out.println("修改后SQL语句:" + newSQL); // 返回执行结果
return invocation.proceed();
} /**
* 为目标对象创建一个代理对象,使用 Plugin.wrap(target,this) 即可
* @param target 上次包装的代理对象
* @return 本次包装过的代理对象
*/
@Override
public Object plugin(Object target) {
System.out.println("ExamplePlugin...plugin:" + target);
return Plugin.wrap(target, this);
} /**
* 获取自定义配置参数
* @param properties
*/
@Override
public void setProperties(Properties properties) {
System.out.println("插件配置信息:" + properties);
System.out.println("someProperty:" + properties.get("someProperty"));
}
}

5.Main

public static void main(String[] args) throws IOException {
SqlSession session = null;
try {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sqlSessionFactory.openSession();
MyUserMapperAnnotation mapper = session.getMapper(MyUserMapperAnnotation.class); System.out.println(mapper.selectMyUser("asdsad"));
} finally {
if (session != null) {
session.close();
}
}
}


http://www.mybatis.org/mybatis-3/zh/configuration.html#plugins

MyBatis-Plugins的更多相关文章

  1. mybatis plugins实现项目【全局】读写分离

    在之前的文章中讲述过数据库主从同步和通过注解来为部分方法切换数据源实现读写分离 注解实现读写分离: http://www.cnblogs.com/xiaochangwei/p/4961807.html ...

  2. mybatis源码配置文件解析之四:解析plugins标签

    在前边的博客在分析了mybatis解析typeAliases标签,<mybatis源码配置文件解析之三:解析typeAliases标签>.下面来看解析plugins标签的过程. 一.概述 ...

  3. springboot整合mybatis开发

    1创建项目,在启动类上添加映射扫描注解 2导入依赖,添加mybatis generator自动生成代码插件 <!-- mybatis generator 自动生成代码插件 --> < ...

  4. 逆水行舟 —— MyBatis

    第一轮总结性笔记 这是一个很漫长的过程,我买了套课程,将在日后记录学习笔记,取名为逆水行舟系列 MyBatis的基础 根据MyBatis的官方介绍: 整个测试项目结构如下:使用Maven架构项目 po ...

  5. mybatis generator自动生成sqlmap代码的不完善之处以及解决方法

    a) 建表时,字段名称建议用"_"分隔多个单词,比如:AWB_NO.REC_ID...,这样生成的entity,属性名称就会变成漂亮的驼峰命名,即:awbNo.recId b)or ...

  6. MyBatis Generator实现MySQL分页插件

    MyBatis Generator是一个非常方便的代码生成工具,它能够根据表结构生成CRUD代码,可以满足大部分需求.但是唯一让人不爽的是,生成的代码中的数据库查询没有分页功能.本文介绍如何让MyBa ...

  7. Mybatis三剑客介绍

    1.MyBatis generator 利用mybatis-generator自动生成代码 下载地址:  https://download.csdn.net/download/qq_36625806/ ...

  8. mybatis(mysql)代码生成器扩展

    前些天在做我的KSF框架的时候需要用到mybatis代码生成器, 但是发现有一些东西需要调整,主要集中在以下几点: 1. 加入batchInsert  2. 加入batchUpdate 3. mysq ...

  9. Mybatis 插件使用及源码分析

    Mybatis 插件 Mybatis插件主要是通过JDK动态代理实现的,插件可以针对接口中的方法进行代理增强,在Mybatis中比较重要的接口如下: Executor :sql执行器,包含多个实现类, ...

  10. Mybatis 懒加载使用及源码分析

    Mybatis 懒加载的使用 什么是懒加载?懒加载的意思就是在使用的时候才去加载,不使用不去加载,相反的就叫饥饿加载或者立即加载.懒加载在Mybatis中一般是存在与联合查询的情况,比如查询一个对象的 ...

随机推荐

  1. BZOJ4514[Sdoi2016]数字配对——最大费用最大流

    题目描述 有 n 种数字,第 i 种数字是 ai.有 bi 个,权值是 ci. 若两个数字 ai.aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数, 那么这两个数字可以配对,并获得 ci ...

  2. 为什么Elasticsearch查询变得这么慢了?

    参考内容:https://mp.weixin.qq.com/s/RTpBaFpNELQCO6VE0KMfsw

  3. 提高SqlServer数据库的安全性,禁用掉sa账户

    Sqlsever 数据库有两种登陆身份验证模式,一种是windows身份验证:一种是sqlserver 账户验证模式,在sqlserver 账户验证模式中,sa账户是大家所熟知的,并且sa也是内置的默 ...

  4. HDU2204 Eddy's爱好

    题意:给你一个正整数N,确定在1到N之间有多少个可以表示成M^K(K>1)的数. 解析:一个数N 开K次根后得到M  则小于M的所有数的K次方一定小于N 因为任何一个合数都能分解为素数的乘积 所 ...

  5. 手机Web 开发中图片img 如何等比例缩放

    如果图片本身没有设置 width.height属性的话,只需要修改 max-width:100%; 就可以了 如果图片本身设置了 width.height属性的话,需要同时修改width 和heigh ...

  6. 【cf842C】 Ilya And The Tree(dfs、枚举因子)

    C. Ilya And The Tree 题意 给一棵树求每个点到根的路上允许修改一个为0,gcd的最大值. 题解 g是从根到当前点允许修改的最大gcd,gs为不修改的最大gcd.枚举当前点的因子,更 ...

  7. 【Luogu3733】[HAOI2017]八纵八横(线性基,线段树分治)

    [Luogu3733][HAOI2017]八纵八横(线性基,线段树分治) 题面 洛谷 题解 看到求异或最大值显然就是线性基了,所以只需要把所有环给找出来丢进线性基里就行了. 然后线性基不资磁撤销?线段 ...

  8. 持久化和公平分发.py

    1.消息持久化在实际应用中,可能会发生消费者收到Queue中的消息,但没有处理完成就宕机(或出现其他意外)的情况,这种情况下就可能会导致消息丢失.为了避免这种情况发生,我们可以要求消费者在消费完消息后 ...

  9. smtp

    新闻系统的定时通知初步有三种实用方式,1.短信 2.邮箱 3.微信 短信就不得不使用第三方平台,虽说5分一条,但耐不住量大,一天1000条的话,50元也是一笔不小的支出. 这时,邮箱和微信的优势就体现 ...

  10. CANOE入门(二)

    CAPL就是Communication Application Programming Laguage的缩写,CAPL类似于C语言的语法,因此所有的语法请参考C语言教程,这里不在这里进行详述,关于C语 ...