目录

  • 一、mybatis 插件介绍
  • 二、mybatis 插件原理
    • 1、创建
    • 2、拦截
      • 1)首先是 ParameterHandler 的创建,在 Configuration 类当中:
      • 2)interceptorChain 保存了所有的拦截器(interceptors),是 mybatis 初始化的时候创建的。
    • 3、应用
      • 1)类相关代码
      • 2)插件配置,在 sqlMapConfig.xml 中
  • 三、自定义插件
    • 1、插件接口
    • 2、自定义插件
      • 1)相关类代码
      • 2)相关配置 sqlMapConfig.xml
      • 3)mapper 接口
      • 4)mapper.xml 配置
      • 5)测试类代码
  • 四、源码分析
    • 1、执行插件逻辑
    • 2、执行自定义插件
  • 五、pageHelper 分页插件
    • 1、导入插件依赖
    • 2、在 mybatis 核心配置文件中配置 PageHelper 插件
    • 3、测试分页代码实现
  • 六、通用 mapper
    • 1、导入插件依赖
    • 2、mybatis 配置文件中完成配置
    • 3、实体类设置主键
    • 4、定义通用 mapper
    • 5、测试代码

一、mybatis 插件介绍

mybatis 插件在四大组件 (Executor、StatementHandler、ParameterHandler、ResultSetHandler) 处提供了简单易用的插件扩展机制。
mybatis 对持久层的操作就是借助于四大核心对象。
myBatis 支持用插件对四大核心对象进行拦截,对 mybatis 来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的动态代理实现的,换句话说,myBatis 中的四大对象都是代理对象。

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

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

二、mybatis 插件原理

1、创建

在四大对象创建的时候:

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

2、拦截

插件具体是如何拦截并附加额外的功能的呢?以 ParameterHandler 来说:

1)首先是 ParameterHandler 的创建,在 Configuration 类当中:

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);return parameterHandler;
}

2)interceptorChain 保存了所有的拦截器(interceptors),是 mybatis 初始化的时候创建的。

public Object pluginAll(Object target) {for (Interceptor interceptor : interceptors) {target = interceptor.plugin(target);}return target;
}

调用拦截器链中的拦截器依次的对目标进行拦截或增强。
interceptor.plugin(target) 中的 target 就可以理解为 mybatis 中的四大对象。返回的 target 是被重重代理后的对象。

3、应用

如果我们想要拦截 Executor 的 query 方法,那么可以这样定义插件:

1)类相关代码

@Intercepts({@Signature(type = Executor.class,method = "query",args = {MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})
})
public class ExeunplePlugin implements Interceptor {//省略逻辑
}

2)插件配置,在 sqlMapConfig.xml 中

<plugins><plugin interceptor="com.lagou.plugin.ExamplePlugin"></plugin>
</plugins>

这样 MyBatis 在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链)中。
待准备工作做完后,MyBatis 处于就绪状态。
我们在执行 SQL 时,需要先通过 DefaultSqlSessionFactory 创建 SqlSession。
Executor 实例会在创建 SqlSession 的过程中被创建, Executor 实例创建完毕后, MyBatis 会通过 JDK 动态代理为实例生成代理类。
这样,插件逻辑即可在 Executor 相关方法被调用前执行。
以上就是 MyBatis 插件机制的基本原理。

三、自定义插件

1、插件接口

Mybatis 插件接口 —— Interceptor

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

2、自定义插件

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

1)相关类代码

@Intercepts(@Signature(    // 注意看这个大花括号,也就这说这里可以定义多个@Signature对多个地方拦截,都用这个拦截器type = StatementHandler.class,    // 这是指拦截哪个接口method = "prepare",    // 这个接口内的哪个方法名,不要拼错了args = {Connection.class,Integer.class}    // 这是拦截的方法的入参,按顺序写到这,不要多也不要少,如果方法重载,可是要通过方法名和入参来确定唯一的
))
public class MyPlugin implements Interceptor {private final Logger logger = LoggerFactory.getLogger(this.getClass());// 这里是每次执行操作的时候,都会进行这个拦截器的方法内@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 增强逻辑System.out.println("增强了参数功能。。");// 执行原方法return invocation.proceed();}/*** 主要是为了把这个拦截器生成一个代理放到拦截器链中。* @Description 包装目标对象,为目标对象创建代理对象。* @Param target 为要拦截的对象* @Return 代理对象*/@Overridepublic Object plugin(Object target) {System.out.println("将要包装的目标对象:"+target);return Plugin.wrap(target,this);}/** 获取配置文件的属性 **/ // 插件初始化的时候调用,也只调用一次,插件配置的属性从这里设置进来@Overridepublic void setProperties(Properties properties) {System.out.println("插件配置的初始化参数:"+properties);}
}

2)相关配置 sqlMapConfig.xml

<plugins><plugin interceptor="com.lagou.plugin.MySqlPagingPlugin"><!--配置参数--><property name="name" value="Bob"/></plugin>
</plugins>

3)mapper 接口

public interface UserMapper {List<User> selectUser();
}

4)mapper.xml 配置

<mapper namespace="com.lagou.mapper.UserMapper"><select id="selectUser" resultType="com.lagou.pojo.User">select id,username from user</select>
</mapper>

5)测试类代码

public class PluginTest {@Testpublic void test() throws IOException {InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = sqlSessionFactory.openSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);List<User> byPaging = userMapper.selectUser();for (User user : byPaging) {System.out.println(user);}}
}

四、源码分析

1、执行插件逻辑

Plugin 实现了 InvocationHandler 接口,因此它的 invoke 方法会拦截所有的方法调用。
invoke 方法会对所拦截的方法进行检测,以决定是否执行插件逻辑。该方法的逻辑如下:

// -Plugin
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {/* * 获取被拦截方法列表,比如:* signatureMap.get(Executor.class), 可能返回 [query, update, commit]*/Set<Method> methods = signatureMap.get(method.getDeclaringClass());// 检测方法列表是否包含被拦截的方法if (methods != null && methods.contains(method)) {// 执行插件逻辑return interceptor.intercept(new Invocation(target, method, args));}// 执行被拦截的方法return method.invoke(target, args);} catch (Exception e) {throw ExceptionUtil.unwrapThrowable(e);}
}

invoke 方法的代码比较少,逻辑不难理解。
invoke 方法会检测被拦截方法是否配置在插件的 @Signature 注解中,若是,则执行插件逻辑,否则执行被拦截方法。

2、执行自定义插件

@Override
public Object intercept(Invocation invocation) throws Throwable {// 增强逻辑System.out.println("增强了参数功能。。");// 执行原方法return invocation.proceed();
}

插件逻辑封装在 intercept 中,该方法的参数类型为 Invocationo Invocation 主要用于存储目标类,方法以及方法参数列表。

public class Invocation {private final Object target;private final Method method;private final Object[] args;public Invocation(Object target, Method method, Object[] args) {this.target = target;this.method = method;this.args = args;}// 省略get方法public Object proceed() throws InvocationTargetException, IllegalAccessException {// 调用被拦截的方法return method.invoke(target, args);}
}

五、pageHelper 分页插件

myBatis 可以使用第三方的插件来对功能进行扩展,分页助手 PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。

1、导入插件依赖

<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>

2、在 mybatis 核心配置文件中配置 PageHelper 插件

<!-- 注意:分页助手的插件,配置在通用馆mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper"><!--指定方言--><property name="dialect" value="mysql"/>
</plugin>

3、测试分页代码实现

@Test
public void testPageHelper() {//设置分页参数PageHelper.startPage(1, 2);List<User> select = userMapper2.select(null); for (User user : select) {System.out.println(user);}//其他分页的相关数据PageInfo<User> pageInfo = new PageInfo<User>(select); System.out.println("总条数:" + pageInfo.getTotal()); System.out.println("总页数:" + pageInfo.getPages ()); System.out.println("当前页:" + pageInfo.getPageNum()); System.out.println("每页显万长度:" + pageInfo.getPageSize()); System.out.println("是否第一页:" + pageInfo.isIsFirstPage()); System.out.println("是否最后一页:" + pageInfo.isIsLastPage());
}

六、通用 mapper

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

1、导入插件依赖

<dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId><version>3.1.2</version>
</dependency>

2、mybatis 配置文件中完成配置

<plugins><!--分页插件:如果有分页插件,要排在通用 mapper 之前--><plugin interceptor="com.github.pagehelper.PageHelper"><property name="dialect" value="mysql"/></plugin><plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor"><!-- 通用 mapper 接口,多个通用接口用逗号隔开 --><property name="mappers" value="tk.mybatis.mapper.common.Mapper"/></plugin>
</plugins>

3、实体类设置主键

@Table(name = "t_user")
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;private String username;
}

4、定义通用 mapper

import com.lagou.domain.User;
import tk.mybatis.mapper.common.Mapper;public interface UserMapper extends Mapper<User> {}

5、测试代码

public class UserTest {@Testpublic void test1() throws IOException {Inputstream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);SqlSession sqlSession = build.openSession();UserMapper userMapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setId(4);// (1) mapper 基础接口// select 接口// 根据实体中的属性进行查询,只能有—个返回值User user1 = userMapper.selectOne(user);// 查询全部结果List<User> users = userMapper.select(null);// 根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号userMapper.selectByPrimaryKey(1); // 根据实体中的属性查询总数,查询条件使用等号userMapper.selectCount(user);// insert 接口// 保存一个实体,null 值也会保存,不会使用数据库默认值int insert = userMapper.insert(user);// 保存实体,null的属性不会保存,会使用数据库默认值int i = userMapper.insertSelective(user); // update 接口// 根据主键更新实体全部字段,null 值会被更新int i1 = userMapper.updateByPrimaryKey(user);// delete 接口// 根据实体属性作为条件进行删除,查询条件使用等号int delete = userMapper.delete(user);// 根据主键字段进行删除,方法参数必须包含完整的主键属性userMapper.deleteByPrimaryKey(1); // (2) example 方法Example example = new Example(User.class);example.createCriteria().andEqualTo("id", 1);example.createCriteria().andLike("val", "1");//自定义查询List<User> users1 = userMapper.selectByExample(example);}
}

文章内容输出来源:拉勾教育Java高薪训练营;

mybatis 插件相关推荐

  1. idea mybatis plugin插件,免费mybatis插件

    idea的mybatis插件.一直想下一个,在大批量修改一些问题时候 mapper和.xml文件查看会方便许多. 直接在idea的插件market里看经常会卡住,直接去网站看. 于是去官网查查看,网站 ...

  2. c++map的使用_mybatis源码 | mybatis插件及动态代理的使用

    学习背景 最近公司在做一些数据库安全方面的事情,如数据库中不能存手机号明文,不能存身份证号明文, 但是项目已经进行了好几个月了, 这时候在应用层面去改显然不太现实, 所以就有了Mybatis的自定义插 ...

  3. Intelij IDEA 2016.3安装mybatis插件并激活教程

    转载自:http://blog.csdn.net/solo_talk/article/details/53540449 现在Mybatis框架越来越受欢迎,Intelij IDEA这个编辑器逐渐成为很 ...

  4. mybatis 插件原理

    [传送门]:mybatis 插件原理 转载于:https://www.cnblogs.com/virgosnail/p/10079838.html

  5. MyBatis 插件原理与自定义插件-猜想

    MyBatis 的插件可以在不修改原来的代码的情况下,通过拦截的方式,改变四大核心对象的行为,比如处理参数,处理SQL,处理结果. 第一个问题: 不修改对象的代码,怎么对对象的行为进行修改,比如说在原 ...

  6. MyBatis(四)MyBatis插件原理

    MyBatis插件原理 MyBatis对开发者非常友好,它通过提供插件机制,让我们可以根据自己的需要去增强MyBatis的功能.其底层是使用了代理模式+责任链模式 MyBatis官方https://m ...

  7. idea mybatis插件_IntelliJ IDEA插件推荐(二)

    上次发过IntelliJ IDEA的插件推荐,详见:IntelliJ IDEA插件推荐,但是由于篇幅有限加上一些网友的推荐和自己这段时间的接触到一些新的非常实用的插件,所以又写一篇插件推荐.废话不多说 ...

  8. 后端技术:mybatis插件原理详解

    关注"Java后端技术全栈" 回复"面试"获取全套面试资料 上次发文说到了如何集成分页插件MyBatis插件原理分析,看完感觉自己better了,今天我们接着来 ...

  9. SpringCloud或SpringBoot+Mybatis-Plus利用mybatis插件实现数据操作记录及更新对比

    引文 本文主要介绍如何使用mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录.在更新数据时准确记录更新字段 核心:mybatis插件(拦截器). ...

  10. 优雅的利用Mybatis插件实现sql查询耗时统计

    优雅的利用Mybatis插件实现sql查询耗时统计 一. Mybatis反射机制讲解 二. 代理模式讲解 静态代理 动态代理 JDK动态代理参考代码 Proxy.newProxyInstance(xx ...

最新文章

  1. 利用Python进行数据分析(7) pandas基础: Series和DataFrame的简单介绍 一、pandas 是什么 pandas 是基于 NumPy 的一个 Python 数据分析包,主
  2. 【Python】 文件和操作文件方法
  3. Google Earth 8.0
  4. C++中public,protected,private派生类继承问题和访问权限问题
  5. 三角形中的“叛徒”--莱洛三角形,一个神奇的存在!
  6. scrapyd部署_第八章 第一节 scrapyd和scrapy-client
  7. consul docker方式搭建
  8. python自然语言处理书籍_精通Python自然语言处理pdf
  9. 两万字深度介绍分布式系统原理,这一篇就够了
  10. Python实现视频语音和字幕自动审查功能
  11. c语言pow函 新闻,c语言pow函数(C语言pow函数)
  12. jsjq面试笔记(上)
  13. tar --exclude用法
  14. python定义一个字符串变量_python基础入门语法和变量类型(一)
  15. 经济学人翻译练习——肯•帕克斯顿的再次竞选是对德克萨斯共和党价值观的考验
  16. 输入两个正整数m和n,输出m到n之间每个整数的自然对数
  17. 兔子机器人Blossom成为萌宠,软体机器人将会是设计新方向?
  18. CocosCreator 音效管理器
  19. loadimage没有与参数列表匹配的重载函数 解决方案
  20. java 给图片加马赛克_java处理图片--图片的缩放,旋转和马赛克化

热门文章

  1. asp.net中实现登陆的时候用SSL
  2. 构造方法传参数的小心得
  3. 计算机软件相关的优秀基金,计算机软件研究所
  4. html自动保存excel,如何在Excel中将Excel Sheet保存为HTML?
  5. 制作VOC格式的数据集
  6. Qt_qDebug 原理详解
  7. python 的库如何开发_Python开发者必备6个基本库
  8. qt制作刻度条(可用作时间及其他刻度)实现缩放,以及平移
  9. php拍照虚线上传图片,照片怎么添加白色虚线 给照片上的人物周围添加虚线描边效果|照片处理工具...
  10. 详细图解MySQL(win7x64 5.7.16版本)下载、安装、配置与使用