关于mybatis:mybatis之执行器

60次阅读

共计 6253 个字符,预计需要花费 16 分钟才能阅读完成。

本篇来聊一下 mybatis 的执行器,看看如何在不同场景应用不同执行器以及不同执行器的实现原理是怎么的(基于 mybatis 3.4.6)。

知识点

  • 什么是执行器
  • mybatis 执行器类型及何时应用
  • 各个执行器的实现原理

    什么是执行器

    顾名思义,执行器就是用来执行咱们的 sql 语句,从而取到后果或对数据库进行更新的货色。它是 mybatis 中十分外围的概念,它提供了增、改、查、事务管理等基本操作接口,基本上所有的货色都是围绕执行器来进行,看下图

    mybatis 执行器类型及何时应用

    执行器类型

    咱们先来看下 mybatis 目前有哪些执行器类型

  • SIMPLE
  • REUSE
  • BATCH
    咱们能配置的目前就以上三种执行器,通过 defaultExecutorType(留神是全局的)来指定应用哪种,如果是通过 xml 形式配置的,则参照官网

    如果是应用 spring boot 集成的,则如下

    上面咱们别离对三种执行器来一一介绍。

    SIMPLE

    能够了解为根本的执行器,这也是 mybatis 的默认执行器,也是咱们平时用的最多的执行器,咱们无需做任何配置更改。它大抵的流程是:关上连贯 -> 设置 Statement -> 参数注入 -> 执行 -> 后果映射 -> 敞开 Statement,能够看到每次用完之后会把 Statement 关掉。

    REUSE

    看名字就晓得这是一个可重用执行器,什么叫可重用呢?指的是 simple 流程中的后面两步能够反复利用,也就是 关上连贯 -> 设置 Statement,这两步会创立一个新的 Statement,reuse 执行器外部保护一个缓存,第一次获取后就会以对应的 sql(占位符 ? 不被具体参数替换)作为 key 进行缓存该 Statement,并且不对 Statement 进行敞开,前面遇到该 sql 只有从第三步开始做就能够了,这样就带来了性能上的优化(实际上优化并不大)。

    BATCH

    同样通过名称就能看进去这是一个批量执行器,批量是什么意思?就是说它是能够一次性执行一批 sql 语句的,次要是针对更新、插入等批改性操作,对于单条或者查问类操作,就不要用这个执行器了,为什么能做到批量执行呢,其实实质上是用的 sql 包里的 PreparedStatementaddBatch(),前面原理局部再细说。

    何时应用

    分明了三种执行器类型的特点之后,咱们再来比照下在不同场景下的性能输入,这样大家就分明何时应用何种类型执行器了,这里没有比照更新操作,是因为插入操作实质上用的就是更新操作接口。基于 spring 应用的 mybatis,基于以下表构造

    单条查问

    先上代码

          DefaultSqlSessionFactory sqlSessionFactory = (DefaultSqlSessionFactory)applicationContext.getBean("sqlSessionFactory");
          // 这里本人抉择执行器类型
          DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE);
          long startTime = System.currentTimeMillis();
    //        userInfoMapper.selectById(22222);
          defaultSqlSession.selectList("com.example.mybatisanalyze.mapper.UserInfoMapper.selectById", 22222);
          long endTime = System.currentTimeMillis();
          System.out.println((endTime - startTime) + "ms");

    simple:

    从图中能够看出花了 42ms
    reuse:
    DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE); 改为 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.REUSE);

    从图中能够看出花了 34ms
    batch:
    DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE); 改为 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.BATCH);

    从图中能够看出花了 43ms
    论断:单条查问三种执行器差不多,然而倡议抉择 simple,起因见执行器类型中阐明。

    单条插入

    先上代码

          DefaultSqlSessionFactory sqlSessionFactory = (DefaultSqlSessionFactory)applicationContext.getBean("sqlSessionFactory");
          // 执行器类型本人抉择
          DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE);
          long startTime = System.currentTimeMillis();
          UserInfo userInfo = new UserInfo();
          userInfo.setNickName("bbbcd");
          userInfo.setUserName("abcdc");
          userInfo.setBirthday(new Date());
          userInfo.setRegisterTime(LocalDateTime.now());
          userInfo.setEmail("12345");
          defaultSqlSession.insert("com.example.mybatisanalyze.mapper.UserInfoMapper.insert", userInfo);
          long endTime = System.currentTimeMillis();
          System.out.println((endTime - startTime) + "ms");

    simple:

    从图中能够看出花了 172ms
    reuse:
    代码中 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE) 这行改为 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.REUSE);

    从图中能够看出花了 178ms
    batch:
    代码中 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE) 这行改为 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.BATCH);

    从图中能够看出花了 176ms
    论断:三种执行器性能差不多,倡议抉择 simple

    批量插入

    先上代码

    DefaultSqlSessionFactory sqlSessionFactory = (DefaultSqlSessionFactory)applicationContext.getBean("sqlSessionFactory");
          // 这里执行器类型本人抉择
          DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE);
          long startTime = System.currentTimeMillis();
          for (int i = 0; i < 10000; i++) {UserInfo userInfo = new UserInfo();
              userInfo.setNickName("bbbc");
              userInfo.setUserName("abcd");
              userInfo.setBirthday(new Date());
              userInfo.setRegisterTime(LocalDateTime.now());
              userInfo.setEmail("1234");
              defaultSqlSession.insert("com.example.mybatisanalyze.mapper.UserInfoMapper.insert", userInfo);
          }
          long endTime = System.currentTimeMillis();
          System.out.println((endTime - startTime) + "ms");

    插入一万条数据
    simple:

    从图中能够看出花了 11762ms
    reuse:
    代码中 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE) 这行改为 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.REUSE);

    从图中能够看出花了 10992ms
    batch:
    代码中 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.SIMPLE) 这行改为 DefaultSqlSession defaultSqlSession = (DefaultSqlSession)sqlSessionFactory.openSession(ExecutorType.BATCH); 并且在 for 循环之后加个 commit 操作 defaultSqlSession.commit();

    从图中能够看出花了 6173ms
    论断:显著应该抉择 batch

    各个执行器的实现原理

    mybatis 和执行器相干的逻辑都在 org.apache.ibatis.executor 包下

    先来看一下执行器接口 org.apache.ibatis.executor.Executor

    能够看到根本就是数据库相干操作,再来看下继承体系

    能够看到咱们所用的三种执行器都是子类,这里用到两种设计模式,别离是 BaseExecutor 中实现的模板模式和 CachingExecutor 中实现的装璜器模式。咱们从入口开始看,入口在 org.apache.ibatis.session.defaults.DefaultSqlSession,这里提供了根本的操作,咱们基于selectOne 办法进行剖析,始终跟到 org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds) 会发现他实质调的就是执行器的查问操作

    而这个执行器哪里生成的呢?在 org.apache.ibatis.session.defaults.DefaultSqlSessionFactory 中咱们调用 openSession 办法时,咱们传入指定的执行器类型,也能够应用默认的,最终会在 org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType) 中创立执行器

    这里能够看到会依据传入的执行器类型来创立对应执行器,默认应用 simple,会应用 CachingExecutor 来进行一层包装。接着下面的 query 函数持续剖析,咱们晓得它默认会走到 org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)

    这里应用了装璜器模式,在原来的执行器性能根底上增加了二级缓存的性能。外面最终是调用 org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql) 进行执行,这里用到了一级缓存,没有缓存的状况下会去数据库查,也就是调用办法 queryFromDatabase

    这里就应用了模板模式,doQuery()由子类本人来实现,各个子类执行器的逻辑绝对比较简单,这里就介绍一下 BatchExecutor,其余能够本人去看

    这里保护了 statementListbatchResultList来记录执行的指令以及寄存对应后果的对象,在 doUpdate 的时候进行记录,最终会在 handler.batch() 中去调用 PreparedStatementaddBatch办法,相当于把指令暂存到预编译器中。后续在咱们做 commit()的时候会去调用 doFlushStatements 办法去做批执行。

    总结

    本篇文章的干货还是比拟多的,基本上将 mybatis 的执行器进行了比拟具体的介绍以及如何选型,当然底层还波及到连接池和 sql 驱动包的一些知识点,连接池前面讲,sql 预编译器批量执行的介绍,能够参考 https://blog.csdn.net/bluelin…

正文完
 0