美文网首页Java进阶之路
MyBatis学习总结二(一级缓存机制)

MyBatis学习总结二(一级缓存机制)

作者: 叫我不矜持 | 来源:发表于2019-01-29 13:36 被阅读79次

    前言

    MyBatis是一个简单,小巧但功能非常强大的ORM开源框架,它的功能强大也体现在它的缓存机制上。MyBatis提供了一级缓存、二级缓存 这两个缓存机制,能够很好地处理和维护缓存,以提高系统的性能。

    一.Mybatis的几个核心概念

    1.SqlSession : 代表和数据库的一次会话,向用户提供了操作数据库的方法。
    2.MappedStatement: 代表要发往数据库执行的指令,可以理解为是Sql的抽象表示。
    3.Executor: 具体用来和数据库交互的执行器,接受MappedStatement作为参数。
    4.映射接口: 在接口中会将要执行的Sql用一个方法来表示,具体的Sql写在映射文件中。
    5.映射文件: 可以理解为是Mybatis编写Sql的地方,通常来说每一张单表都会对应着一个映射文件,在该文件中会定义Sql语句入参和出参的形式。

    二.getMapper()的使用时的内部执行流程

    mapper元素代表这个文件是一个映射文件,使用namespace和具体的映射接口绑定起来,namespace的值就是这个接口的全限定类名。select|insert|update|delete代表的是Sql语句,映射接口中定义的每一个方法也会和映射文件中的语句通过id的方式绑定起来,方法名就是语句的id,同时会定义语句的入参和出参,用于完成和Java对象之间的转换。

    在Mybatis初始化的时候,每一个语句都会使用对应的MappedStatement代表,使用namespace+语句本身的id来代表这个语句。

    SELEC  Tid,name,age  FROM  student  WHERE  id= #{id}

    在Mybatis执行时,会进入对应接口的方法,通过类名加上方法名的组合生成id,找到需要的MappedStatement,交给执行器使用。

    三.什么是一级缓存? 为什么使用一级缓存?

    每当我们使用MyBatis开启一次和数据库的会话时,MyBatis会创建出一个SqlSession对象表示一次数据库会话;在对数据库的会话过程中,我们可能会反复执行完全相同的查询语句;
    如果不采取一些措施,我们每一次查询都会查询一次数据库,而如果在极短的时间内做了很多次相同的查询操作,那么这些查询返回的结果很可能相同,但由于对数据库进行一次操作的开销是很大的,这样会极大的浪费资源;

    为了解决这一问题,减少资源的浪费,MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询了。

    一个SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会首先尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户否则从数据库读取数据,将查询结果存入缓存并返回给用户。

    一级缓存配置
    <setting name="localCacheScope" value="SESSION"/>

    四.MyBatis的一级缓存是如何组织的?

    SqlSession只是一个MyBatis对外的接口,SqlSession将它的工作交给了Executor执行器这个角色来完成,负责完成对数据库的各种操作。当创建了一个SqlSession对象时,MyBatis会为这个SqlSession对象创建一个新的Executor执行器,而缓存信息就被维护在这个Executor执行器中,MyBatis将缓存和对缓存相关的操作封装成了Cache接口中。

    SqlSession和Executor以及Cache关系图

    如上述的类图所示,Executor接口的实现类BaseExecutor中拥有一个Cache接口的实现类PerpetualCache,则对于BaseExecutor对象而言,它将使用PerpetualCache对象维护缓存。

    PerpetualCache实现原理其实很简单,其内部就是通过一个简单的HashMap<k,v> 来实现的,没有其他的任何限制

    五.一级缓存的生命周期

    [1]. MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

    [2]. 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;

    [3]. 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;

    [4].SqlSession中执行了任何一个DML对数据库进行操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;

     SqlSession 一级缓存的工作流程:

    一级缓存执行的时序图

    1.对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果;
    2. 判断从Cache中根据特定的key值取的数据数据是否为空,即是否命中;
    3. 如果命中,则直接将缓存结果返回;
    4. 如果没命中:       
        4.1  去数据库中查询数据,得到查询结果;       
        4.2  将key和查询到的结果分别作为key,value对存储到Cache中;       
        4.3. 将查询结果返回;
    5. 判断缓存级别是否为STATEMENT级别,如果是的话,清空本地缓存


    5. Cache接口的设计以及CacheKey的定义

    MyBatis内部有很多Cache接口的实现,一级缓存只会涉及到一个PerpetualCache子类.

    Cache最核心的实现其实就是一个Map,将本次查询使用的特征值作为key,将查询结果作为value存储到Map中.

    MyBatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询:

    1. 传入的 statementId
    2. 查询时要求的结果集中的结果范围 (结果的范围通过rowBounds.offset和rowBounds.limit表示)
    3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() )
    4. 传递给java.sql.Statement要设置的参数值

    Tips:
        MyBatis自身提供的分页功能是通过RowBounds来实现的,它通过rowBounds.offsetrowBounds.limit来过滤查询出来的结果集,这种分页功能是基于查询结果的再过滤,而不是进行数据库的物理分页;

    综上所述,CacheKey由以下条件决定:
                    statementId  + rowBounds  + 传递给JDBC的SQL  + 传递给JDBC的参数值

    构建CacheKey的过程实际上就是构造其hashCode的过程

    6.总结

    1.Mybatis一级缓存的生命周期和SqlSession一致。

    2.Mybatis的缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念,同时只是使用了默认的、简单的hashmap,也没有做容量上的限定。

    3.Mybatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,有操作数据库写的话,会引起脏数据,建议是把一级缓存的默认级别设定为Statement,即不使用一级缓存

    相关文章

      网友评论

        本文标题:MyBatis学习总结二(一级缓存机制)

        本文链接:https://www.haomeiwen.com/subject/rhgzjqtx.html