Mybatis源码分析(一):设计要点与核心架构分析

版权声明:非商业目的可自由转载,转载请标明出处 https://blog.csdn.net/u010013573/article/details/87874292

设计初衷与目的

  • 在mybatis的框架出现之前,在Java中存在两种方式来进行数据库操作:

    • 第一种为JDBC,这种方式的缺点就是需要在应用代码中加载数据库驱动,创建数据库连接,创建执行语句,使用数据库连接来执行语句,获取返回结果,解析返回结果,关闭数据库连接等操作,所以是比较底层的操作,使用复杂度较高,并且每个有SQL操作的地方都要重复按照这个逻辑来定义代码,造成代码冗余;

    • 第二种方式为Hibernate,Hibernate为实现POJO和数据表的映射,基于实体类POJO来实现数据表的所有操作,在应用代码中对于针对单个表的CRUD相关操作,一般不需要显式实现SQL。但是如果要自定义SQL操作实现复杂查询,则要

      (1)基于Criteria的相关API以面向对象的方式来自定义SQL操作,或者:

      (2)基于SQLQuery来直接使用SQL,但是这两种方式使用都比较繁琐,一个简单的SQL需要编写大量的代码,同时对于复杂的SQL,如需要拼表操作等,需要借助更多的其他API。

      所以不管基于Criteria还是SQLQuery,除了使用复杂度高之外,还有就是使用门槛较高,需要记住和理解所有这些API,一个简单的SQL需要花费大半天和大量代码来实现。其次,针对表的操作由hibernate自动提供SQL实现,不便于进行SQL优化。

      同时在Hibenate中,基于SQLQuery来直接使用SQL,如果涉及到比较多的条件判断,则一般是在应用代码内部结合ifelse这些来判断,判断完成之后直接定义SQL,而不能在应用代码之外的一个统一的地方来管理这些SQL和实现这种基于条件来动态生成SQL,所以与应用代码的业务逻辑耦合在一起了。

  • 基于以上两种方式的不足分析,mybatis框架在以上两种方式之间取了一个折中:

    (1)首先在框架内部包装了JDBC的相关操作,在框架底层设计中,基于JDBC来实现底层的数据库操作;

    (2)其次在框架上层设计中,实现POJO和SQL语句之间的映射,即一个POJO对应一个独立的业务实体,业务实体是在业务角度的一个完整个体,如用户User或者一个复合体UserAndCompany,而不仅限于是一个数据库表;POJO的每个方法对应一个完整的SQL操作;

    (3)数据库相关操作从应用代码的业务逻辑中抽离出来统一管理,同时支持动态SQL定义。应用代码只依赖POJO的方法来指定需要执行的SQL操作,由POJO的方法来间接调用对应的SQL获取结果并解析,作为该方法的返回值。

业务流程

  • 业务流程主要是从应用代码配置POJO和SQL的映射关系,到一个SQL语句执行,获取执行结果的完整过程来分析:
    1. 应用代码SQL的定义和配置与POJO的映射关系;
    2. 框架初始化加载POJO,POJO的方法和SQL的映射信息;
    3. 应用代码获取POJO对象并调用其方法;
    4. 通过框架加载好的POJO的方法和SQL的映射信息,获取该POJO的方法对应的SQL;
    5. 从数据源获取一个数据库连接;
    6. 通过该数据库连接执行该SQL;
    7. 获取SQL执行结果并解析。

核心设计

  • 以下结合业务流程的顺序来分析mybatis的包设计:

    1. binding:POJO和业务实体的映射MapperProxy,POJO的方法和SQL的映射MapperMethod;
    2. builder,binding和mapping:builder解析xml或者注解,将POJO生成binding包对应的MapperProxy,POJO的方法对应binding包的MapperMethod,MapperMethod为懒加载;SQL相关信息,包括SQL语句,SQL参数与参数类型,SQL执行结果类型等使用mapping包的MappedStatement作为主对象,内部包含ParameterMap,ResultMap等来对应SQL参数,SQL执行结果类型等;
    3. session,应用代码的POJO接口:应用代码主要是通过session包获取SqlSession对象,然后通过应用代码的POJO接口来获取mapper对象,调用这个mapper对象的方法;
    4. binding,mapping:在内部加载实现中,应用代码的POJO对象加载存放在binding包的MapperProxy对象,方法调用则是通过MapperProxy对象懒加载生成MapperMethod并调用;
    5. session,datasource:数据库连接主要是通过sqlSessionFactory在生成sqlSession时,使用sqlSessionFactory内部维护的datasource包定义的数据源dataSource引用来获取数据库连接,将该连接绑定到sqlSession。同一个sqlSession对象使用同一个数据库连接发送一个或多个SQL语句给数据执行。
    6. executor的statement子包,parameter子包,type:SQL的执行主要在executor包定义,每个sqlSession绑定一个独立的executor,executor先通过parameter包的ParameterHandler和type包来处理SQL的参数绑定,其中ParameterHandler依赖type包,type包主要是提供SQL对应的JDBC的Statement的各个参数的赋值操作,以及返回结果中jdbcType和javaType之间的转换;然后通过StatementHandler来执行该SQL语句,包括调用底层的JDBC来发起数据库操作请求;
    7. executor的result,resultset子包:SQL执行结果的解析注意在executor的result,resultset子包完成。
  • 除了业务流程相关的设计外,还包含相关功能支持的包:

    1. cache:缓存,包括一级和二级缓存实现定义;
    2. reflection:类的Method,Field,类型等相关反射操作方法的定义;主要是对SQL参数类型,执行结果等的解析提供反射支持;还包括mapper接口的方法参数和mapper.xml的SQL的动态参数的映射处理(ParamNameResolver);
    3. transaction:数据库事务定义,简单定义了数据库的四种事务,在应用中通常使用Spring的事务机制;
    4. parsing:xml文件的节点解析,xml的SQL内容解析,如#{}等的解析;
    5. scripting的xmltags子包:xml的动态SQL节点定义,如if,else节点定义。
  • 相关架构与源码对应关系示意图如下:(图片引用自:MyBatis架构设计及源代码分析系列(一):MyBatis架构

  • 架构示意图:
    在这里插入图片描述

  • 源码示意图:
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u010013573/article/details/87874292