框架原理与源码解析: MyBatis_No4 面试简述

MyBatis简析

一、框架及原理分析

MyBatis是支持定制化SQL、存储过程以及高级映射的持久层框架,主要完成2件事情。

     1,封装JDBC

     2,利用反射打通Java类与SQL之间的相互转换。

MyBatis的主要设计目的是让对执行SQL语句时输入输出的数据管理更加方便,所以方便地写出SQL和方便地获取SQL的执行结果,才是MyBatis的核心竞争力

二、基本构成

一、配置文件     4大标签     +2小标签

配置标签:<configuration>      根标签

   1    ## 设置标签:<settings> :        <setting name=""  value="true\false">  

         ####name:缓存能力cacheEnabled、延迟加载lazyLoadingEnabled、

   2    ## 类型别名:<typeAliases>:<typeAlias  type="Bean"  alias=""  >

   3    ## 环境标签:<environments  default = "">:<environment  id="">

         #####事务管理:<transactionManager  type="JDBC\">

         #####数据源:    <dataSource  type="POOLED">:<property >

   4    ## 映射标签:<mappers>:<mapper resource="BeanMapper.xml">

二、主要构件

Configuration          储存配置文件的配置信息

SqlSession             表示和数据库交互时的会话,完成必要数据库的增删改查。

Executor                 生成SQL语句+维护查询缓存

StatementHandler  操作JDBC Statement

ParameterHandler  传入参数 [ Java类型 ],转换,JDBC Statement数据类型    [long->bingint]

ResultSetHandler   JDBC返回的ResultSet结果集对象,转换,List类型的集合

TypeHandler           Java数据类型,映射、转换,JDBC 数据类型(=表_列_类型)

MappedStatement  维护一条<select|update|delete|insert>节点的封装

SqlSource               根据传入参数parameterObject,动态生成SQL+信息封装到BoundSql对象+并返回

BoundSql                表示动态生成的SQL语句以及相应的参数信息

补充:以上主要成员在一次数据库操作中基本都会涉及,在SQL操作重点需要关注SQL参数什么时候被设置和结果集怎么转换为JavaBean对象,这两个过程正好对应StatementHandler和ResultSetHandler类中的处理逻辑。

三、MyBatis缓存

MyBatis提供查询缓存,用于减轻数据库压力,提高性能。

一级缓存:SqlSession级别            =不共享                                          默认开启

        ##  每个SqlSession对象都有一个哈希表用于缓存数据。

        ##  同一SqlSession对象执行2次,第一次查询完毕,将结果缓存。第二次查询,直接返回缓存结果。

二级缓存:Mapper(namespace)级别=   多sqlSession共享                   默认关闭

        ##  多个SqlSession对象共享二级缓存,跨SqlSession

        ##  不同SqlSession对象执行2次相同SQL语句,第一次查询完毕,将结果缓存。第二次查询,直接返回二级缓存结果。

SQL语句更新操作

        ## 当SQL语句进行(删除/添加/更新),会清空对应的缓存,保证缓存中存储的信息都是最新的数据。

        ##  MyBatis的弱势:二级缓存对细粒度的数据级别的实现不友好,比如(如下需求:对商品信息进行缓存,由于商品信息查询访问量大,但要求用户每次能查询最新的商品信息,此时如果使用mybatis的二级缓存就无法实现当一个商品变化时只刷新该商品信息,而不刷新其他商品信息,因为mybatis的二级缓存区域以mapper为单位划分,当一个商品信息变化时,会将所有商品信息的缓存数据全部清空。解决此类问题需要在业务层根据需求对数据有针对性缓存,具体业务具体实现)=类似同步...

四、源码流程分析

第一步,MyBatis的初始化:  解析配置文件+初始化Configuration

       ### 加载配置文件                  InputStream inputStream = Resources.getResourceAsStream( resource);

       ### 构建sqlSessionFactory   sessionFactory = new SqlSessionFactory Builder().build(inputStream);

       ###  首先会创建SqlSessionFactory建造者对象,然后由它创建SqlSessionFactory。

                这里用到的是建造者模式,建造者模式最简单的理解就是不手动new对象,而是由其他类来进行对象的创建。

       ###  XMLConfigBuilder对象进行XML配置文件解析,实际为configuration节点的解析操作。节点的依次解析

         [Node:  properties 、settings、typeAliases、plugins、objectFactory、objectWrapperFactory、reflectorFactory、

          environments、databaseIdProvider、typeHandlers、mappers ]

       ###  在configuration节点下会依次解析properties/settings/.../mappers等节点配置。在解析environments节点时,会根据transactionManager的配置来创建事务管理器,根据dataSource的配置来创建DataSource对象,这里面包含了数据库登录的相关信息。在解析mappers节点时,会读取该节点下所有的mapper文件,然后进行解析,并将解析后的结果存到configuration对象中。

       ###  解析完MyBatis配置文件后,configuration就初始化完成了,然后根据configuration对象来创建SqlSession,到这里时,MyBatis的初始化的征程已经走完了。。

第二步,MyBatis的SQL查询流程:

       ### 通过封装JDBC进行操作,然后使用Java反射技术完成JavaBean对象到数据库参数之间的相互转换,这种映射关系就是TypeHandler对象来完成。在获取数据表对应的元数据时,会保存该表所有列的数据库类型。

       ###  DefaultSqlSessin类 : 创建sqlSession的过程其实就是根据configuration中的配置来创建对应的类,然后返回创建的sqlSession对象调用selectOne方法进行SQL查询,selectOne方法最后调用的是selectList,在selectList中,会查询configuration中存储的MappedStatement对象,mapper文件中一个sql语句的配置对应一个MappedStatement对象,然后调用执行器进行查询操作。

       ###  执行器在query操作中,优先会查询缓存是否命中,命中则直接返回,否则从数据库中查询。

      [  CachingExecutor类:获取关联参数的sql,boundSql、创建cache key值、

               queryFromDatabase()方法:获取二级缓存实例

               queryFromDatabase()方法:先往localCache中插入一个占位对象,再往缓存中写入数据,也就是缓存查询结果 ]

       ###真正的doQuery操作是由SimplyExecutor代理来完成的,该方法中有2个子流程,一个是SQL参数的设置,另一个是SQL查询操作和结果集的封装。

       #####SimpleExecutor类

        子流程1:SQL查询参数的设置    stmt = prepareStatement(handler, ms.getStatementLog());

        子流程2:SQL查询操作               return handler.<E>query(stmt, resultHandler);

       ### 子流程1 Sql查询参数的设置

       ######SimpleExecutor类:首先获取Connection连接,然后准备statement,然后就设置SQL查询中的参数值。

       [  打开一个connection连接,在使用完后不会close,而是存储下来,当下次需要打开连接时就直接返回。 ]

       ######DefaultParameterHandler类: 设置SQL参数值,从ParameterMapping中读取参数值和类型,然后设置到SQL语句中

       ### 子流程2 Sql查询结果集的封装

       ##### ResultSetWrapper是ResultSet的包装类,调用getFirstResultSet方法获取第一个ResultSet,同时获取数据库的MetaData数据,包括数据表列名、列的类型、类序号等,这些信息都存储在ResultSetWrapper类中了。然后调用handleResultSet方法来来进行结果集的封装。

        #####  调用handleRowValues方法进行结果值的设置

        #####  mapping.typeHandler.getResult会获取查询结果值的实际类型,比如我们user表中id字段为int类型,那么它就对应Java中的Integer类型,然后通过调用statement.getInt("id")来获取其int值,其类型为Integer。metaObject.setValue方法会把获取到的Integer值设置到Java类中的对应字段。

        ##### metaValue.setValue方法最后会调用到Java类中对应数据域的set方法,这样也就完成了SQL查询结果集的Java类封装过程。最后贴一张调用栈到达Java类的set方法中的快照:

猜你喜欢

转载自blog.csdn.net/ddhmbbklyk2018/article/details/82502476