MyBatis是目前十分风行的ORM框架,它的性能很弱小,然而其实现却比较简单、优雅。本文次要讲述MyBatis的架构设计思路,并且探讨MyBatis的几个核心部件,而后联合一个select查问实例,深刻代码,来探索MyBatis的实现。

一、MyBatis的框架设计

        注:上图很大水平上参考了iteye 上的chenjc_it 所写的博文原理剖析之二:框架整体设计 中的MyBatis架构体图,chenjc_it总结的十分好,赞一个!

1.接口层---和数据库交互的形式

MyBatis和数据库的交互有两种形式:

a.应用传统的MyBatis提供的API;

b. 应用Mapper接口

1.1.应用传统的MyBatis提供的API

这是传统的传递Statement Id 和查问参数给 SqlSession 对象,应用 SqlSession对象实现和数据库的交互;MyBatis 提供了十分不便和简略的API,供用户实现对数据库的增删改查数据操作,以及对数据库连贯信息和MyBatis 本身配置信息的保护操作。

                  

上述应用MyBatis 的办法,是创立一个和数据库打交道的SqlSession对象,而后依据Statement Id 和参数来操作数据库,这种形式诚然很简略和实用,然而它不合乎面向对象语言的概念和面向接口编程的编程习惯。因为面向接口的编程是面向对象的大趋势,MyBatis 为了适应这一趋势,减少了第二种应用MyBatis 反对接口(Interface)调用形式。

1.2. 应用Mapper接口

MyBatis 将配置文件中的每一个<mapper> 节点形象为一个 Mapper 接口,而这个接口中申明的办法和跟<mapper> 节点中的<select|update|delete|insert> 节点项对应,即<select|update|delete|insert> 节点的id值为Mapper 接口中的办法名称,parameterType 值示意Mapper 对应办法的入参类型,而resultMap 值则对应了Mapper 接口示意的返回值类型或者返回后果集的元素类型。

依据MyBatis 的配置标准配置好后,通过SqlSession.getMapper(XXXMapper.class) 办法,MyBatis 会依据相应的接口申明的办法信息,通过动静代理机制生成一个Mapper 实例,咱们应用Mapper 接口的某一个办法时,MyBatis 会依据这个办法的办法名和参数类型,确定Statement Id,底层还是通过SqlSession.select("statementId",parameterObject);或者SqlSession.update("statementId",parameterObject); 等等来实现对数据库的操作,(_至于这里的动静机制是怎么实现的,我将筹备专门一片文章来探讨,敬请关注~_)

MyBatis 援用Mapper 接口这种调用形式,纯正是为了满足面向接口编程的须要。(其实还有一个起因是在于,面向接口的编程,使得用户在接口上能够应用注解来配置SQL语句,这样就能够脱离XML配置文件,实现“0配置”)。

2.数据处理层

数据处理层能够说是MyBatis 的外围,从大的方面上讲,它要实现三个性能:

a. 通过传入参数构建动静SQL语句;

b. SQL语句的执行以及封装查问后果集成List<E>

2.1.参数映射和动静SQL语句生成

动静语句生成能够说是MyBatis框架十分优雅的一个设计,MyBatis 通过传入的参数值,应用 Ognl 来动静地结构SQL语句,使得MyBatis 有很强的灵活性和扩展性。

参数映射指的是对于java 数据类型和jdbc数据类型之间的转换:这里有包含两个过程:查问阶段,咱们要将java类型的数据,转换成jdbc类型的数据,通过 preparedStatement.setXXX() 来设值;另一个就是对resultset查问后果集的jdbcType 数据转换成java 数据类型。

(_至于具体的MyBatis是如何动静构建SQL语句的,我将筹备专门一篇文章来探讨,敬请关注~_)

2.2. SQL语句的执行以及封装查问后果集成List<E>

动静SQL语句生成之后,MyBatis 将执行SQL语句,并将可能返回的后果集转换成List<E> 列表。MyBatis 在对后果集的解决中,反对后果集关系一对多和多对一的转换,并且有两种反对形式,一种为嵌套查问语句的查问,还有一种是嵌套后果集的查问。

3. 框架撑持层

3.1. 事务管理机制

事务管理机制对于ORM框架而言是不可短少的一部分,事务管理机制的品质也是考量一个ORM框架是否优良的一个规范,对于数据管理机制我曾经在我的博文《深刻了解mybatis原理》 MyBatis事务管理机制 中有十分具体的探讨,感兴趣的读者能够点击查看。

3.2. 连接池管理机制

因为创立一个数据库连贯所占用的资源比拟大, 对于数据吞吐量大和访问量十分大的利用而言,连接池的设计就显得十分重要,对于连接池管理机制我曾经在我的博文《深刻了解mybatis原理》 Mybatis数据源与连接池 中有十分具体的探讨,感兴趣的读者能够点击查看。

3.3. 缓存机制

为了进步数据利用率和减小服务器和数据库的压力,MyBatis 会对于一些查问提供会话级别的数据缓存,会将对某一次查问,搁置到SqlSession 中,在容许的工夫距离内,对于完全相同的查问,MyBatis 会间接将缓存后果返回给用户,而不必再到数据库中查找。(_至于具体的MyBatis缓存机制,我将筹备专门一篇文章来探讨,敬请关注~_)

3. 4. SQL语句的配置形式

传统的MyBatis 配置SQL 语句形式就是应用XML文件进行配置的,然而这种形式不能很好地反对面向接口编程的理念,为了反对面向接口的编程,MyBatis 引入了Mapper接口的概念,面向接口的引入,对应用注解来配置SQL 语句成为可能,用户只须要在接口上增加必要的注解即可,不必再去配置XML文件了,然而,目前的MyBatis 只是对注解配置SQL 语句提供了无限的反对,某些高级性能还是要依赖XML配置文件配置SQL 语句。

4 疏导层

疏导层是配置和启动MyBatis 配置信息的形式。MyBatis 提供两种形式来疏导MyBatis :基于XML配置文件的形式和基于Java API 的形式,读者能够参考我的另一片博文:Java Persistence with MyBatis 3(中文版) 第二章 疏导MyBatis

   

二、MyBatis的次要构件及其互相关系

从MyBatis代码实现的角度来看,MyBatis的次要的核心部件有以下几个:

  • SqlSession 作为MyBatis工作的次要顶层API,示意和数据库交互的会话,实现必要数据库增删改查性能
  • Executor MyBatis执行器,是MyBatis 调度的外围,负责SQL语句的生成和查问缓存的保护
  • StatementHandler 封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement后果集转换成List汇合。
  • ParameterHandler 负责对用户传递的参数转换成JDBC Statement 所须要的参数,
  • ResultSetHandler 负责将JDBC返回的ResultSet后果集对象转换成List类型的汇合;
  • TypeHandler 负责java数据类型和jdbc数据类型之间的映射和转换
  • MappedStatement MappedStatement保护了一条<select|update|delete|insert>节点的封装,
  • SqlSource 负责依据用户传递的parameterObject,动静地生成SQL语句,将信息封装到BoundSql对象中,并返回
  • BoundSql 示意动静生成的SQL语句以及相应的参数信息
  • Configuration MyBatis所有的配置信息都维持在Configuration对象之中。

(注:这里只是列出了我集体认为属于外围的部件,请读者不要先入为主,认为MyBatis就只有这些部件哦!每个人对MyBatis的了解不同,剖析出的后果天然会有所不同,欢送读者提出质疑和不同的意见,咱们独特探讨~)

它们的关系如下图所示:

三、从MyBatis一次select 查问语句来剖析MyBatis的架构设计


一、数据筹备(十分相熟和利用过MyBatis 的读者能够迅速浏览此节即可)

     1. 筹备数据库数据,创立EMPLOYEES表,插入数据:

     --创立一个员工根本信息表          create  table "EMPLOYEES"(              "EMPLOYEE_ID" NUMBER(6) not null,            "FIRST_NAME" VARCHAR2(20),             "LAST_NAME" VARCHAR2(25) not null,             "EMAIL" VARCHAR2(25) not null unique,             "SALARY" NUMBER(8,2),              constraint "EMP_EMP_ID_PK" primary key ("EMPLOYEE_ID")          );          comment on table EMPLOYEES is '员工信息表';          comment on column EMPLOYEES.EMPLOYEE_ID is '员工id';          comment on column EMPLOYEES.FIRST_NAME is 'first name';          comment on column EMPLOYEES.LAST_NAME is 'last name';          comment on column EMPLOYEES.EMAIL is 'email address';          comment on column EMPLOYEES.SALARY is 'salary';          --增加数据          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (100, 'Steven', 'King', 'SKING', 24000.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (101, 'Neena', 'Kochhar', 'NKOCHHAR', 17000.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (102, 'Lex', 'De Haan', 'LDEHAAN', 17000.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (103, 'Alexander', 'Hunold', 'AHUNOLD', 9000.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (104, 'Bruce', 'Ernst', 'BERNST', 6000.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (105, 'David', 'Austin', 'DAUSTIN', 4800.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (106, 'Valli', 'Pataballa', 'VPATABAL', 4800.00);          insert into EMPLOYEES (EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY)          values (107, 'Diana', 'Lorentz', 'DLORENTZ', 4200.00); 

 

  2. 配置Mybatis的配置文件,命名为mybatisConfig.xml:

  <?xml version="1.0" encoding="utf-8"?>      <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"      "http://mybatis.org/dtd/mybatis-3-config.dtd">      <configuration>        <environments default="development">          <environment id="development">            <transactionManager type="JDBC" />            <dataSource type="POOLED">           <property name="driver" value="oracle.jdbc.driver.OracleDriver" />      1          <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />                 <property name="username" value="louis" />                 <property name="password" value="123456" />            </dataSource>          </environment>        </environments>         <mappers>            <mapper  resource="com/louis/mybatis/domain/EmployeesMapper.xml"/>         </mappers>      </configuration>    

3.     创立Employee实体Bean 以及配置Mapper配置文件

 package com.louis.mybatis.model;      import java.math.BigDecimal;     public class Employee {         private Integer employeeId;         private String firstName;         private String lastName;         private String email;         private BigDecimal salary;         public Integer getEmployeeId() {              return employeeId;        }         public void setEmployeeId(Integer employeeId) {             this.employeeId = employeeId;         }        public String getFirstName() {            return firstName;          }         public void setFirstName(String firstName) {          this.firstName = firstName;         }        public String getLastName() {            return lastName;       }       public void setLastName(String lastName) {            this.lastName = lastName;       }        public String getEmail() {             return email;         }          public void setEmail(String email) {              this.email = email;        }         public BigDecimal getSalary() {            return salary;          }          public void setSalary(BigDecimal salary) {              this.salary = salary;          }      }    
  <?xml version="1.0" encoding="UTF-8" ?>      <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >      <mapper namespace="com.louis.mybatis.dao.EmployeesMapper" >        <resultMap id="BaseResultMap" type="com.louis.mybatis.model.Employee" >          <id column="EMPLOYEE_ID" property="employeeId" jdbcType="DECIMAL" />          <result column="FIRST_NAME" property="firstName" jdbcType="VARCHAR" />          <result column="LAST_NAME" property="lastName" jdbcType="VARCHAR" />          <result column="EMAIL" property="email" jdbcType="VARCHAR" />          <result column="SALARY" property="salary" jdbcType="DECIMAL" />        </resultMap>       <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >          select               EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY              from LOUIS.EMPLOYEES              where EMPLOYEE_ID = #{employeeId,jdbcType=DECIMAL}        </select>      </mapper>    

4. 创立eclipse 或者myeclipse 的maven我的项目,maven配置如下:

  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">       <modelVersion>4.0.0</modelVersion>       <groupId>batis</groupId>       <artifactId>batis</artifactId>        <version>0.0.1-SNAPSHOT</version>       <packaging>jar</packaging>        <name>batis</name>        <url>http://maven.apache.org</url>       <properties>          <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        </properties>       <dependencies>         <dependency>           <groupId>junit</groupId>            <artifactId>junit</artifactId>           <version>3.8.1</version>           <scope>test</scope>          </dependency>          <dependency>                  <groupId>org.mybatis</groupId>                <artifactId>mybatis</artifactId>                  <version>3.2.7</version>         </dependency>         <dependency>              <groupId>com.oracle</groupId>              <artifactId>ojdbc14</artifactId>             <version>10.2.0.4.0</version>         </dependency>       </dependencies>      </project>    

5. 客户端代码:

1.  package com.louis.mybatis.test;    3.  import java.io.InputStream;    4.  import java.util.HashMap;    5.  import java.util.List;    6.  import java.util.Map;    8.  import org.apache.ibatis.io.Resources;    9.  import org.apache.ibatis.session.SqlSession;    10.  import org.apache.ibatis.session.SqlSessionFactory;    11.  import org.apache.ibatis.session.SqlSessionFactoryBuilder;    13.  import com.louis.mybatis.model.Employee;    15.  /**    16.   * SqlSession 简略查问演示类    17.   * @author louluan    18.   */    19.  public class SelectDemo {    21.      public static void main(String[] args) throws Exception {    22.          /*    23.   * 1.加载mybatis的配置文件,初始化mybatis,创立出SqlSessionFactory,是创立SqlSession的工厂    24.   * 这里只是为了演示的须要,SqlSessionFactory长期创立进去,在理论的应用中,SqlSessionFactory只须要创立一次,当作单例来应用    25.   */    26.          InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");    27.          SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();    28.          SqlSessionFactory factory = builder.build(inputStream);    30.          //2. 从SqlSession工厂 SqlSessionFactory中创立一个SqlSession,进行数据库操作    31.          SqlSession sqlSession = factory.openSession();    33.          //3.应用SqlSession查问    34.          Map<String,Object> params = new HashMap<String,Object>();    36.          params.put("min_salary",10000);    37.          //a.查问工资低于10000的员工    38.          List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);    39.          //b.未传最低工资,查所有员工    40.          List<Employee> result1 = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary");    41.          System.out.println("薪资低于10000的员工数:"+result.size());    42.          //~output :   查问到的数据总数:5     43.          System.out.println("所有员工数: "+result1.size());    44.          //~output :  所有员工数: 8    45.      }    47.  }    

二、SqlSession 的工作过程剖析:

1. 开启一个数据库拜访会话---创立SqlSession对象:

SqlSession sqlSession = factory.openSession();

           MyBatis封装了对数据库的拜访,把对数据库的会话和事务管制放到了SqlSession对象中。

     

2. 为SqlSession传递一个配置的Sql语句 的Statement Id和参数,而后返回后果:

List<Employee> result = sqlSession.selectList("com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",params);

上述的"com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary",是配置在EmployeesMapper.xml 的Statement ID,params 是传递的查问参数。

让咱们来看一下sqlSession.selectList()办法的定义:

 1.    public <E> List<E> selectList(String statement, Object parameter) {    2.      return this.selectList(statement, parameter, RowBounds.DEFAULT);    3.    }    5.    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    6.      try {    7.        //1.依据Statement Id,在mybatis 配置对象Configuration中查找和配置文件绝对应的MappedStatement     8.        MappedStatement ms = configuration.getMappedStatement(statement);    9.        //2. 将查问工作委托给MyBatis 的执行器 Executor    10.        List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);    11.        return result;    12.      } catch (Exception e) {    13.        throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    14.      } finally {    15.        ErrorContext.instance().reset();    16.      }    17.    }

MyBatis在初始化的时候,会将MyBatis的配置信息全副加载到内存中,应用org.apache.ibatis.session.Configuration实例来保护。使用者能够应用sqlSession.getConfiguration()办法来获取。MyBatis的配置文件中配置信息的组织格局和内存中对象的组织格局简直齐全对应的。上述例子中的

 1.    <select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" >    2.      select     3.          EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY    4.          from LOUIS.EMPLOYEES    5.          <if test="min_salary != null">    6.              where SALARY < #{min_salary,jdbcType=DECIMAL}    7.          </if>    8.    </select>

加载到内存中会生成一个对应的MappedStatement对象,而后会以key="com.louis.mybatis.dao.EmployeesMapper.selectByMinSalary" ,valueMappedStatement对象的模式保护到Configuration的一个Map中。当当前须要应用的时候,只须要通过Id值来获取就能够了。

从上述的代码中咱们能够看到SqlSession的职能是:

SqlSession依据Statement ID, 在mybatis配置对象Configuration中获取到对应的MappedStatement对象,而后调用mybatis执行器来执行具体的操作。

3.MyBatis执行器Executor依据SqlSession传递的参数执行query()办法(因为代码过长,读者只需浏览我正文的中央即可):

1.  /**    2.  * BaseExecutor 类局部代码    3.  *    4.  */    5.  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {    7.      // 1.依据具体传入的参数,动静地生成须要执行的SQL语句,用BoundSql对象示意     8.      BoundSql boundSql = ms.getBoundSql(parameter);    9.      // 2.为以后的查问创立一个缓存Key    10.      CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);    11.      return query(ms, parameter, rowBounds, resultHandler, key, boundSql);    12.   }    14.    @SuppressWarnings("unchecked")    15.    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    16.      ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());    17.      if (closed) throw new ExecutorException("Executor was closed.");    18.      if (queryStack == 0 && ms.isFlushCacheRequired()) {    19.        clearLocalCache();    20.      }    21.      List<E> list;    22.      try {    23.        queryStack++;    24.        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;    25.        if (list != null) {    26.          handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);    27.        } else {    28.          // 3.缓存中没有值,间接从数据库中读取数据     29.          list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);    30.        }    31.      } finally {    32.        queryStack--;    33.      }    34.      if (queryStack == 0) {    35.        for (DeferredLoad deferredLoad : deferredLoads) {    36.          deferredLoad.load();    37.        }    38.        deferredLoads.clear(); // issue #601    39.        if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {    40.          clearLocalCache(); // issue #482    41.        }    42.      }    43.      return list;    44.    }    45.   private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    46.      List<E> list;    47.      localCache.putObject(key, EXECUTION_PLACEHOLDER);    48.      try {    50.        //4. 执行查问,返回List 后果,而后    将查问的后果放入缓存之中    51.        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);    52.      } finally {    53.        localCache.removeObject(key);    54.      }    55.      localCache.putObject(key, list);    56.      if (ms.getStatementType() == StatementType.CALLABLE) {    57.        localOutputParameterCache.putObject(key, parameter);    58.      }    59.      return list;    60.    }    
1.  /**    2.  *    3.  *SimpleExecutor类的doQuery()办法实现    4.  *    5.  */    6.    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {    7.      Statement stmt = null;    8.      try {    9.        Configuration configuration = ms.getConfiguration();    10.        //5. 依据既有的参数,创立StatementHandler对象来执行查问操作    11.        StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);    12.        //6. 创立java.Sql.Statement对象,传递给StatementHandler对象    13.        stmt = prepareStatement(handler, ms.getStatementLog());    14.        //7. 调用StatementHandler.query()办法,返回List后果集    15.        return handler.<E>query(stmt, resultHandler);    16.      } finally {    17.        closeStatement(stmt);    18.      }    19.    }    

上述的Executor.query()办法几经转折,最初会创立一个StatementHandler对象,而后将必要的参数传递给StatementHandler,应用StatementHandler来实现对数据库的查问,最终返回List后果集。

从下面的代码中咱们能够看出,Executor的性能和作用是:

**(1、依据传递的参数,实现SQL语句的动静解析,生成BoundSql对象,供StatementHandler应用;
**

(2、为查问创立缓存,以进步性能(具体它的缓存机制不是本文的重点,我会独自拿进去跟大家探讨,感兴趣的读者能够关注我的其余博文);

(3、创立JDBC的Statement连贯对象,传递给StatementHandler对象,返回List查问后果。

4. StatementHandler对象负责设置Statement对象中的查问参数、解决JDBC返回的resultSet,将resultSet加工为List 汇合返回:

     接着下面的Executor第六步,看一下:prepareStatement() 办法的实现:

1.  /**    2.  *    3.  *SimpleExecutor类的doQuery()办法实现    4.  *    5.  */    6.  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 1.筹备Statement对象,并设置Statement对象的参数 stmt = prepareStatement(handler, ms.getStatementLog()); // 2. StatementHandler执行query()办法,返回List后果 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }    8.    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {    9.      Statement stmt;    10.      Connection connection = getConnection(statementLog);    11.      stmt = handler.prepare(connection);    12.      //对创立的Statement对象设置参数,即设置SQL 语句中 ? 设置为指定的参数    13.      handler.parameterize(stmt);    14.      return stmt;    15.    }    

    以上咱们能够总结StatementHandler对象次要实现两个工作:

(1. 对于JDBCPreparedStatement类型的对象,创立的过程中,咱们应用的是SQL语句字符串会蕴含 若干个? 占位符,咱们其后再对占位符进行设值。

StatementHandler通过parameterize(statement)办法对Statement进行设值;

(2.StatementHandler通过List<E> query(Statement statement, ResultHandler resultHandler)办法来实现执行Statement,和将Statement对象返回的resultSet封装成List

5.   StatementHandler 的parameterize(statement) 办法的实现:

1.  /**    2.  *   StatementHandler 类的parameterize(statement) 办法实现     3.  */    4.  public void parameterize(Statement statement) throws SQLException {    5.      //应用ParameterHandler对象来实现对Statement的设值     6.      parameterHandler.setParameters((PreparedStatement) statement);    7.    }    
 1.    /**    2.   *     3.   *ParameterHandler类的setParameters(PreparedStatement ps) 实现    4.   * 对某一个Statement进行设置参数    5.   */    6.    public void setParameters(PreparedStatement ps) throws SQLException {    7.      ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    8.      List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();    9.      if (parameterMappings != null) {    10.        for (int i = 0; i < parameterMappings.size(); i++) {    11.          ParameterMapping parameterMapping = parameterMappings.get(i);    12.          if (parameterMapping.getMode() != ParameterMode.OUT) {    13.            Object value;    14.            String propertyName = parameterMapping.getProperty();    15.            if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params    16.              value = boundSql.getAdditionalParameter(propertyName);    17.            } else if (parameterObject == null) {    18.              value = null;    19.            } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {    20.              value = parameterObject;    21.            } else {    22.              MetaObject metaObject = configuration.newMetaObject(parameterObject);    23.              value = metaObject.getValue(propertyName);    24.            }    26.            // 每一个Mapping都有一个TypeHandler,依据TypeHandler来对preparedStatement进行设置参数    27.            TypeHandler typeHandler = parameterMapping.getTypeHandler();    28.            JdbcType jdbcType = parameterMapping.getJdbcType();    29.            if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();    30.            // 设置参数    31.            typeHandler.setParameter(ps, i + 1, value, jdbcType);    32.          }    33.        }    34.      }    35.    }

从上述的代码能够看到,StatementHandler 的parameterize(Statement) 办法调用了 ParameterHandler的setParameters(statement) 办法,

ParameterHandler的setParameters(Statement)办法负责 依据咱们输出的参数,对statement对象的 ? 占位符处进行赋值。

6.   StatementHandler 的List<E> query(Statement statement, ResultHandler resultHandler)办法的实现:

 1.    /**    2.   * PreParedStatement类的query办法实现    3.   */    4.    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {    5.      // 1.调用preparedStatemnt。execute()办法,而后将resultSet交给ResultSetHandler解决     6.      PreparedStatement ps = (PreparedStatement) statement;    7.      ps.execute();    8.      //2. 应用ResultHandler来解决ResultSet    9.      return resultSetHandler.<E> handleResultSets(ps);    10.    }
1.  /**     2.  *ResultSetHandler类的handleResultSets()办法实现    3.  *    4.  */    5.  public List<Object> handleResultSets(Statement stmt) throws SQLException {    6.      final List<Object> multipleResults = new ArrayList<Object>();    8.      int resultSetCount = 0;    9.      ResultSetWrapper rsw = getFirstResultSet(stmt);    11.      List<ResultMap> resultMaps = mappedStatement.getResultMaps();    12.      int resultMapCount = resultMaps.size();    13.      validateResultMapsCount(rsw, resultMapCount);    15.      while (rsw != null && resultMapCount > resultSetCount) {    16.        ResultMap resultMap = resultMaps.get(resultSetCount);    18.        //将resultSet    19.        handleResultSet(rsw, resultMap, multipleResults, null);    20.        rsw = getNextResultSet(stmt);    21.        cleanUpAfterHandlingResultSet();    22.        resultSetCount++;    23.      }    25.      String[] resultSets = mappedStatement.getResulSets();    26.      if (resultSets != null) {    27.        while (rsw != null && resultSetCount < resultSets.length) {    28.          ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);    29.          if (parentMapping != null) {    30.            String nestedResultMapId = parentMapping.getNestedResultMapId();    31.            ResultMap resultMap = configuration.getResultMap(nestedResultMapId);    32.            handleResultSet(rsw, resultMap, null, parentMapping);    33.          }    34.          rsw = getNextResultSet(stmt);    35.          cleanUpAfterHandlingResultSet();    36.          resultSetCount++;    37.        }    38.      }    40.      return collapseSingleResultList(multipleResults);    41.    }    

从上述代码咱们能够看出,StatementHandler 的List<E> query(Statement statement, ResultHandler resultHandler)办法的实现,是调用了ResultSetHandler的handleResultSets(Statement) 办法。ResultSetHandler的handleResultSets(Statement) 办法会将Statement语句执行后生成的resultSet 后果集转换成List<E> 后果集:

 1.    //    2.    // DefaultResultSetHandler 类的handleResultSets(Statement stmt)实现     3.    //HANDLE RESULT SETS    4.    //    6.    public List<Object> handleResultSets(Statement stmt) throws SQLException {    7.      final List<Object> multipleResults = new ArrayList<Object>();    9.      int resultSetCount = 0;    10.      ResultSetWrapper rsw = getFirstResultSet(stmt);    12.      List<ResultMap> resultMaps = mappedStatement.getResultMaps();    13.      int resultMapCount = resultMaps.size();    14.      validateResultMapsCount(rsw, resultMapCount);    16.      while (rsw != null && resultMapCount > resultSetCount) {    17.        ResultMap resultMap = resultMaps.get(resultSetCount);    19.        //将resultSet    20.        handleResultSet(rsw, resultMap, multipleResults, null);    21.        rsw = getNextResultSet(stmt);    22.        cleanUpAfterHandlingResultSet();    23.        resultSetCount++;    24.      }    26.      String[] resultSets = mappedStatement.getResulSets();    27.      if (resultSets != null) {    28.        while (rsw != null && resultSetCount < resultSets.length) {    29.          ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);    30.          if (parentMapping != null) {    31.            String nestedResultMapId = parentMapping.getNestedResultMapId();    32.            ResultMap resultMap = configuration.getResultMap(nestedResultMapId);    33.            handleResultSet(rsw, resultMap, null, parentMapping);    34.          }    35.          rsw = getNextResultSet(stmt);    36.          cleanUpAfterHandlingResultSet();    37.          resultSetCount++;    38.        }    39.      }    41.      return collapseSingleResultList(multipleResults);    42.    }

因为上述的过程时序图太过简单,就不贴出来了,读者能够下载MyBatis源码, 应用Eclipse、Intellij IDEA、NetBeans 等IDE集成环境创立我的项目,Debug MyBatis源码,一步步跟踪MyBatis的实现,这样对学习MyBatis框架很有帮忙~