• MyBatis插件开发


    MyBatis插件开发

    一、前言

      MyBatis在四大对象的创建过程中,都会有插件进行介入。插件可以利用动态代理机制一层层的包装目标对象,而实现在目标对象执行目标方法之前进行拦截的效果。

      MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用。

      默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

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

    二、插件开发

      编写插件实现Interceptor接口,并使用@Intercepts注解完成插件签名

       在全局配置文件中注册插件

      插件原理

      按照插件注解声明,按照插件配置顺序调用插件plugin方法,生成被拦截对象的动态代理

      多个插件依次生成目标对象的代理对象,层层包裹,先声明的先包裹;形成代理链

      目标方法执行时依次从外到内执行插件的intercept方法。

      多个插件情况下,我们往往需要在某个插件中分离出目标对象。可以借助MyBatis提供的SystemMetaObject类来进行获取最后一层的h以及target属性的值

      Interceptor接口

      Intercept:拦截目标方法执行

      plugin:生成动态代理对象,可以使用MyBatis提供的Plugin类的wrap方法

      setProperties:注入插件配置时设置的属性

      常用代码:从代理链中分离真实被代理对象

      //1、分离代理对象。由于会形成多次代理,所以需要通过一个while循环分离出最终被代理对象,从而方便提取信息
      MetaObject metaObject = SystemMetaObject.forObject(target);
      while(metaObject.hasGetter("h")) {
        Object h = metaObject.getValue("h");
        metaObject = SystemMetaObject.forObject(h);
      }
      //2、获取到代理对象中包含的被代理的真实对象
      Object obj = metaObject.getValue("target");
      //3、获取被代理对象的MetaObject方便进行信息提取
      MetaObject forObject = SystemMetaObject.forObject(obj);

    三、MyBatis实用场景

      PageHelper插件进行分页

      批量操作

      存储过程

      typeHandler处理枚举

      PageHelper插件进行分页

      PageHelper是MyBatis中非常方便的第三方分页插件。

      官方文档:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/README_zh.md

      我们可以对照官方文档的说明,快速的使用插件:

      1、导入相关包pagehelper-x.x.x.jar和jsqlparser-0.9.5.jar。
      2、在MyBatis全局配置文件中配置分页插件。

      3、使用PageHelper提供的方法进行分页
      4、可以使用更强大的PageInfo封装返回结果

      批量操作

      默认的openSession() 方法没有参数,它会创建有如下特性的:

      ①会开启一个事务(也就是不自动提交)
      ②连接对象会从由活动环境配置的数据源实例得到。
      ③事务隔离级别将会使用驱动或数据源的默认设置。
      ④预处理语句不会被复用,也不会批量处理更新。

      openSession 方法的ExecutorType类型的参数,枚举类型:

      ①ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情(这是默认装配的)。它为每个语句的执行创建一个新的预处理语句。
      ②ExecutorType.REUSE: 这个执行器类型会复用预处理语句。
      ③ExecutorType.BATCH: 这个执行器会批量执行所有更新语句

      批量操作我们是使用MyBatis提供的BatchExecutor进行的,他的底层就是通过jdbc攒sql的方式进行的。我们可以让他攒够一定数量后发给数据库一次。

      publicvoidtest01() {
        SqlSession openSession = build.openSession(ExecutorType.BATCH);
        UserDao mapper = openSession.getMapper(UserDao.class);
        longstart = System.currentTimeMillis();
        for(inti = 0; i < 1000000; i++) {
          String name = UUID.randomUUID().toString().substring(0, 5);
          mapper.addUser(newUser(null, name, 13));
        }
        openSession.commit();
        openSession.close();
        longend = System.currentTimeMillis();
        System.out.println("耗时时间:"+(end-start));
      }

      100万记录添加测试结果:消耗时间:75583

      与Spring整合中,我们推荐,额外的配置一个可以专门用来执行批量操作的sqlSession

      需要用到批量操作的时候,我们可以注入配置的这个批量SqlSession。通过他获取到mapper映射器进行操作。

      注意:
      1、批量操作是在session.commit()以后才发送sql语句给数据库进行执行的
      2、如果我们想让其提前执行,以方便后续可能的查询操作获取数据,我们可以使用sqlSession.flushStatements()方法,让其直接冲刷到数据库进行执行。

      存储过程

      实际开发中,我们通常也会写一些存储过程,MyBatis也支持对存储过程的调用

      一个最简单的存储过程
      delimiter $$
      create procedure test()
      begin
      select 'hello';
      end $$
      delimiter ;

      存储过程的调用
      1、select标签中statementType=“CALLABLE”
      2、标签体中调用语法:{call procedure_name(#{param1_info},#{param2_info})}

      存储过程-游标处理:MyBatis对存储过程的游标提供了一个JdbcType=CURSOR的支持,可以智能的把游标读取到的数据,映射到我们声明的结果集中

      调用实例:

      publicclassPageEmp {
        private int start;
        private int end;
        private int count;
        private List<Emp> emps;
      <environment id="oracle_dev">
        <transactionManager type="JDBC" />
        <dataSource type="POOLED">
          <property name="driver" value="${orcl.driver}" />
          <property name="url" value="${orcl.url}" />
          <property name="username" value="${orcl.username}" />
          <property name="password" value="${orcl.password}" />
        </dataSource>
      </environment>
      orcl.driver=oracle.jdbc.OracleDriver
      orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
      orcl.username=scott
      orcl.password=123456

      <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
      </databaseIdProvider>

      自定义TypeHandler处理枚举

      我们可以通过自定义TypeHandler的形式来在设置参数或者取出结果集的时候自定义参数封装策略:

      ①实现TypeHandler接口或者继承BaseTypeHandler
      ②使用@MappedTypes定义处理的java类型,使用@MappedJdbcTypes定义jdbcType类型
      ③在自定义结果集标签或者参数处理的时候声明使用自定义TypeHandler进行处理,或者在全局配置TypeHandler要处理的javaType

      测试实例:一个代表部门状态的枚举类

      1、测试全局配置EnumOrdinalTypeHandler

      2、测试全局配置EnumTypeHandler

      3、测试参数位置设置自定义TypeHandler

      自定义TypeHandler

    如果,您对我的这篇博文有什么疑问,欢迎评论区留言,大家互相讨论学习。
    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
    如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
    如果,您对我的博文感兴趣,可以关注我的后续博客,我是【AlbertRui】。

    转载请注明出处和链接地址,欢迎转载,谢谢!

  • 相关阅读:
    go语言笔记——append是内置的函数!!!new是一个函数!!!调试可以使用闭包,本质上是print调试,尼玛!
    go语言笔记——defer作用DB资源等free或实现调试
    提高mysql查询效率的六种方法
    无线网络wifi (WPA/WPA2)密码破解方法
    wifi密码破解方法总结(含破解软件下载链接)
    linux 创建账户
    vim中设置tab的长度的方法
    JAVAC 命令使用方法
    Linux环境下编译并执行ava helloworld程序
    java hadoop file system API
  • 原文地址:https://www.cnblogs.com/albertrui/p/8488959.html
Copyright © 2020-2023  润新知