mybatis源码分析3

一、sql执行流程

根据sql语句使用xml进行维护或者在注解上配置,sql语句执行的入口分为两种:
  第一种,调用org.apache.ibatis.session.SqlSession的crud方法比如selectList/selectOne传递完整的语句id直接执行;
  第二种,先调用SqlSession的getMapper()方法得到mapper接口的一个实现,然后调用具体的方法。除非早期,现在实际开发中,我们一般采用这种方式。

我们看下第一种方式执行selectOne方法

public <T> T selectOne(String statement) {
    return this.<T>selectOne(statement, null);
  }

  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

最终都是委托给了selectList执行

 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
      return result;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

默认是交给SimpleExectutor执行器进行执行
在这里插入图片描述

SQL语句执行方式二 SqlSession.getMapper实现

从configuration中根据type获取mapper的代理对象

public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }

MapperRegistry又将创建代理的任务委托给MapperProxyFactory,MapperProxyFactory首先为Mapper接口创建了一个实现了InvocationHandler方法调用处理器接口的代理类MapperProxy,并实现invoke接口(其中为mapper各方法执行sql的具体逻辑),最后才调用JDK的
java.lang.reflect.Proxy为Mapper接口创建动态代理类并返回

 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null)
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

动态sql
只要mybatis的的crud语句中包含了、等标签或者 之 后 , 就 已 经 算 是 动 态 s q l 了 , 所 以 只 要 在 m y b a t i s 加 载 m a p p e r 文 件 期 间 被 解 析 为 非 S t a t i c S q l S o u r c e , 就 会 被 当 做 动 态 s q l 处 理 , 在 执 行 s e l e c t X X X 或 者 u p d a t e / i n s e r t / d e l e t e 期 间 , 就 会 调 用 对 应 的 S q l N o d e 接 口 和 T e x t S q l N o d e . i s D y n a m i c ( ) 处 理 各 自 的 标 签 以 及 {}之后,就已经算是动态sql了,所以只要在mybatis加载mapper文件期间被解析为非StaticSqlSource,就会被当做动态sql处理,在执行selectXXX或者update/insert/delete期间,就会调用对应的SqlNode接口和TextSqlNode.isDynamic()处理各自的标签以及 sqlmybatismapperStaticSqlSourcesqlselectXXXupdate/insert/deleteSqlNodeTextSqlNode.isDynamic(){},并最终将每个sql片段处理到StaticTextSqlNode并生成最终的参数化静态SQL语句为止。所以,可以说,在绝大部分非PK查询的情况下,我们都是在使用动态SQL。

Mybatis一级与二级缓存

一级缓存
mybatis的一级缓存是SqlSession级别的缓存,在操作数据库的时候需要先创建SqlSession会话对象,在对象中用一个HashMap来存储数据,此HashMap是当前会话对象私有的,别的SqlSession会话对象是无法访问的
流程:

第一次执行select完毕会将查到的数据写入SqlSession内的HashMap中缓存起来,key是查询的完成sql语句,里面有参数,不同的参数是不同的key
第二次执行select会从缓存中查询数据,如果select相同并且参数一样,那么就能从缓存汇总返回数据,不用去查数据库了,从而提高效率

二级缓存
二级缓存是mapper级别的缓存,当多个SqlSession使用同一个Mapper操作数据库的时候,得到的数据会缓存在同一个缓存区域。
springboot项目中使用的配置
在启动类中加上@EnableCaching注解
在需要缓存的mapper中加上@CacheNamespace(implementation = MybatisRedisCache.class)这个注解和二级缓存的类就可以了

对于一级缓存,commit/rollback都会清空一级缓存。
对于二级缓存,DML操作或者显示设置语句层面的flushCache属性都会使得二级缓存失效。

在二级缓存容器的具体回收策略实现上,有下列几种:

LRU – 最近最少使用的:移除最长时间不被使用的对象,也是默认的选项,其实现类是org.apache.ibatis.cache.decorators.LruCache。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们,其实现类是org.apache.ibatis.cache.decorators.FifoCache。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象,其实现类是org.apache.ibatis.cache.decorators.SoftCache。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象,其实现类是org.apache.ibatis.cache.decorators.WeakCache。

三、小结

mybatis在执行期间,主要有四大核心接口对象:

1、执行器Executor,执行器负责整个SQL执行过程的总体控制。
2、参数处理器ParameterHandler,参数处理器负责PreparedStatement入参的具体设置。
3、语句处理器StatementHandler,语句处理器负责和JDBC层具体交互,包括prepare语句,执行语句,以及调用ParameterHandler.parameterize()设置参数。
4、结果集处理器ResultSetHandler,结果处理器负责将JDBC查询结果映射到java对象。

执行器Executor
我们在应用层通过sqlSession执行的各类selectXXX和增删改操作在做了动态sql和参数相关的封装处理后,都被委托给具体的执行器去执行,包括一、二级缓存的管理,事务的具体管理,Statement和具体JDBC层面优化的实现等等。所以执行器比较像是sqlSession下的各个策略工厂实现,用户通过配置决定使用哪个策略工厂。

拦截器Interceptor

public interface Interceptor {
  //执行代理类方法
  Object intercept(Invocation invocation) throws Throwable;
  // 用于创建代理对象
  Object plugin(Object target);
  // 插件自定义属性
  void setProperties(Properties properties);
}

mybatis提供了为插件配置提供了两个注解:org.apache.ibatis.plugin.Signature和org.apache.ibatis.plugin.Intercepts。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值