框架原理与源码解析: MyBatis_No1架构与源码

架构与源码

接口层        :  接口调用

数据处理层 :  SQL解析执行

框架支撑层 :  SQL配置

引导层         :  配置、启动MyBaits

一、框架设计     4  层

接口层:        接口调用方式:  基于Statement ID、  基于Mapper接口

数据处理层: 参数映射、SQL解析、SQL执行、结果处理和映射

框架支撑层: SQL语句配置方式:  基于XML配置            基于注解    [  事务管理、连接池管理、缓存机制 ]  

引导层:        基于XML配置方式         基于Java API方式

扫描二维码关注公众号,回复: 3089296 查看本文章

#接口层:数据增删改查接口、配置信息维护接口

#数据处理层:

##参数映射 ParamterHandler:             参数映射配置、参数映射解析、参数类型解析

##SQL解析 SqlSource:                        SQL语句配置、SQL语句解析、SQL语句动态生成

##SQL执行 Excecutor:                         SimpleExecutor、BatchExecutor、ReuseExecutor

##结果处理和映射 ResultSetHandler:  结果映射配置、结果类型转换、结果类型转换

1.接口层--持久层与数据库交互

交互方式:

       a.使用传统的MyBatis提供的API

       b.使用Mapper接口

1.使用传统的MyBatis提供的API:;

##传统的传递Statement Id 和查询参数给SqlSession对象,使用SqlSession对象完成和数据库的交互;MyBatis提供的非常方便和简单的API,供用户实现对数据库的增删改查数据操作,以及对数据库连接信息和MyBatis自身配置信息的维护操作。

## 工作模式:   

2.使用Mapper接口     

##配置文件<mapper>节点抽象为Mapper接口,而这个几口声明的方法和<mapper>节点中的<select|update|delete|insert>节点对应

###  Mapper 配置文件                 :Mapper接口

###  <mapper namespace="">     :  interface XxxMapper

###  id值                                      :方法名称                

###  parameterTyoe                      : 入参类型

###  resuleMap                             : 返回值类型  or  返回结果集的元素类型

###  原理:

       根据MyBatis的配置规范配置好后,通过SqlSession.getMapper(XxxMapper.class)方法,MyBatis会根据相应的接口声明的方法信息,通过动态代理机制生成一个Mapper实例,我们使用Mapper接口一个方法时,MyBatis会根据方法名称和参数类型,确定Statement Id,底层通过SqlSession.select("statementId",parameterObject);实现对数据库的操作。

### 目的:面向接口编程,即用户在接口上使用注解来配置SQL语句,脱离XML配置文件。

2.数据处理层

核心:a. 通过传入参数,构建动态SQL语句

           b. SQL语句执行

           c.  封装查询结果集成List<E>

1. 参数映射和动态SQL语句生成

###  <1  动态SQL:

###  <2  参数映射:java数据类型与jdbc数据类型的转换

#####  查询阶段:java类型的数据,转换成jdbc类型的数据,并设值preparedStatrment.setXxx();

#####   :resultset 查询结果集的jdbcType数据转换成java数据类型

2. SQL语句的执行以及封装查询结果集成List<E>

###   动态SQL语句生成之后,MyBatis将执行SQL语句,并将可能返回的结果集转换成List<E> 列表。MyBatis在对结果集的处理种,支持结果集一对多和多对一的转换,并且有两种支持方式 :嵌套查询语句的查询、嵌套结果集的查询。

3.框架支撑层

##1,事务管理机制        

##2,连接池管理机制    数据吞吐量、访问量、      [ 数据源与连接池 ] 

##3,缓存机制               提升数据利用率                 [ 服务器与数据库 ]

### [ MyBatis对一些查询提供会话级别的数据缓存,会将对某一次查询,放置到SqlSession,在允许的时间间隔内,完全相同的查询,MyBatis会直接将缓存结果返回给用户,而不再到数据库查找。]

##4,SQL语句的配置方式

## <1  XML配置:  XML配置文件

#### 高级功能:主键返回

## <2 注解配置:  支持有限,高级功能仍需要XML配置。

#### Mapper接口+注解==>注解配置SQL语句

4.引导层

引导层:配置和启动MyBatis配置信息的方式。

## 1. 基于XML配置文件的方式

## 2. 基于Java API的方式

二、主要构件及相互关系

一、主要构件

SqlSession                  与数据库交互,完成必要的增删改查                                       [ 会话,  交互  ]                     [ 顶层API ]

Executor                      生成SQL语句,维护查询缓存                                                  [ 执行器, 调度 ]

StatementHandler       操作JDBC Statement:设置参数、Statement结果集转换成List集合    [ 命令处理器,封装JDBC Statement ]         

ParameterHandler       转换传入参数JDBC Statement 所需参数                           [参数处理器]

TypeHandler                映射、转换:Java数据类型与JDBC数据类型                          [类型处理器]

MappedStatement       维护、封装:<select|update|delete|insert>节点                      [映射语句]

SqlSource                    根据传入的parameterObject,动态生成SQL语句,                [ Sql源]

                                     将信息封装到BoundSql对象,并返回

BoundSql                     表示:动态生成的SQL语句以及相应的参数信息                      [Sql绑定]

Configuration               维持:MyBatis配置信息

二、相互关系

#第一步    SqlSession                                                会话访问:增删改查

#第二步    Executor                                                   查询缓存↓,               传入SQL↑            ,

#第三步    StatementHandler                                     执行Java查询语句↓   传入Java结果集↑

##第四步  ParameterHandler  ResultSetHandler      参数设值↓                   结果集转换↑

##第五步  TypeHandler<T>                                       Java->JDBC↓             JDBC->Java↑

#第六步    JDBC          Statement    ResultSet          Statement     ——>     ResultSet

####Statement:PrepareStatement\ SimpleStatement\ CallableStatement

#A  BoundSql

#B  MappedStatement  :SqlSource   ResultMap       

#C  Configuration

三、案例

一、数据准备

#1. 数据库数据      DATABASE-TABLE

#2. 配置配置文件   MyBatisConfig.xml

#3. 创建数据对象   Bean    :

      创建映射文件   Mapper:<mapper  namespace="BeanMapper">                                            [映射声明:命名空间]

               ### A <resultMap id="BaseResultMap"、type="Bean">                                                  [ 结果映射:主键映射+字段映射]

               #####<id       column="主键"、property="属性"、jdbcType>                                                      [主键映射]

               #####<result column="字段"、property="属性"、jdbcType>                                                      [字段映射]

               ### B <method     id="methodType"、resultMap="BaseResultMap"、parameterType>  [映射方法:结果映射+传参类型]

#4.  配置项目文件  POM:<properties>\<dependencies>
#5.  客户端查询    

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

###2.创建会话工厂     #建造者      SqlSessionFactoryBuilder  builder = new SqlSessionFactoryBuilder;

                                    #建造工厂  SqlSessionFactory factory = builder.build( inputStream );

###3.创建会话             #开启会话  SqlSession sqlSession = factory.openSession();

###4.数据操作             sqlSession.methodName();

Map<String,Object> params = new HashMap<>(String,Object);
//a.查询工资低于10000的员工
List<Employee> result = sqlSession.selectList("com.mybatis.dao.EmployeesMapper.selectByMinSalary",params);
//b.未传最低工资,查所有员工
List<Employee> result1 = sqlSession.selectList("com.mybatis.dao.EmployeesMapper.selectByMinSalary");
System.out.println("薪资低于10000的员工数:"+result.size());
//~output : 查询到的数据总数:5
System.out.println("所有员工数: "+result1.size());
//~output : 所有员工数: 8
二、SqlSession过程分析

1.开启数据库访问会话--创建SqlSession对象。                  

    ##即:SqlSession对象,封装一次数据库会话访问—>实现事务控制和数据操作

2. SqlSession,传递配置的Sql语句 的Statement Id和params,返回结果。

    ##第一步,传入statement[Java命令],通过Statement Id,                           【configuration】

                     在mybatis配置对象,查找对应的MappedStatement [JDBC命令]
    ##第二步,将任务委托给执行器                                                                     【Executor】

   [  MyBatis在初始化的时候,会将MyBatis的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration实例来维护。使用者可以使用sqlSession.getConfiguration()方法来获取。MyBatis的配置文件中配置信息的组织格式和内存中对象的组织格式几乎完全对应的。]

3. Excutor,根据SqlSession传递的params执行query()方法。

    ##Executor.query()方法几经转折,最后创建一个StatementHandler对象,然后将必要的参数传递给StatementHandler,使用StatementHandler来完成对数据库的查询,最终返回List结果集。

4. StatementHandler对象负责设置Statement对象中的查询参数、处理JDBC返回的resultSet,将resultSet加工为List 集合返回。

    ## prepareStatement() 

5. StatementHandler 的parameterize(statement) 方法

    ##StatementHandler 的parameterize(Statement) 方法调用了 ParameterHandler的setParameters(statement) 方法,

    ##ParameterHandler的setParameters(Statement)方法负责 根据输入参数,对statement对象的 ? 占位符处进行赋值。

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

    ##StatementHandler 的List<E> query(Statement statement, ResultHandler resultHandler)方法的实现,是调用了ResultSetHandler的handleResultSets(Statement) 方法。ResultSetHandler的handleResultSets(Statement) 方法会将Statement语句执行后生成的resultSet 结果集转换成List<E> 结果集。

###query()

    ## 1. 调用preparedStatemnt。execute()方法,然后将resultSet交给ResultSetHandler处理

    ## 2. 使用ResultHandler来处理ResultSet

###handleResultSets()

源码:

1.  开启一个数据库访问会话---创建SqlSession对象,

SqlSession sqlSession = factory.openSession();

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()方法的定义: 

  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }
 
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //1.根据Statement Id,在mybatis 配置对象Configuration中查找和配置文件相对应的MappedStatement	
      MappedStatement ms = configuration.getMappedStatement(statement);
      //2. 将查询任务委托给MyBatis 的执行器 Executor
      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();
    }
  }

MyBatis在初始化的时候,会将MyBatis的配置信息全部加载到内存中,使用org.apache.ibatis.session.Configuration实例来维护。使用者可以使用sqlSession.getConfiguration()方法来获取。MyBatis的配置文件中配置信息的组织格式和内存中对象的组织格式几乎完全对应的。上述例子中的

  <select id="selectByMinSalary" resultMap="BaseResultMap" parameterType="java.util.Map" >
    select 
    	EMPLOYEE_ID, FIRST_NAME, LAST_NAME, EMAIL, SALARY
    	from LOUIS.EMPLOYEES
    	<if test="min_salary != null">
    		where SALARY < #{min_salary,jdbcType=DECIMAL}
    	</if>
  </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()方法(由于代码过长,读者只需阅读我注释的地方即可):

/**
* BaseExecutor 类部分代码
*/
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
	  
	// 1.根据具体传入的参数,动态地生成需要执行的SQL语句,用BoundSql对象表示  
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 2.为当前的查询创建一个缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
 
  @SuppressWarnings("unchecked")
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
    	// 3.缓存中没有值,直接从数据库中读取数据  
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    return list;
  }
 private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
    	
      //4. 执行查询,返回List 结果,然后	将查询的结果放入缓存之中
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
/*
*SimpleExecutor类的doQuery()方法实现
*/
  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();
      //5. 根据既有的参数,创建StatementHandler对象来执行查询操作
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //6. 创建java.Sql.Statement对象,传递给StatementHandler对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      //7. 调用StatementHandler.query()方法,返回List结果集
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

上述的Executor.query()方法几经转折,最后会创建一个StatementHandler对象,然后将必要的参数传递给StatementHandler,使用StatementHandler来完成对数据库的查询,最终返回List结果集。

4. StatementHandler对象负责设置Statement对象中的查询参数、处理JDBC返回的resultSet,将resultSet加工为List 集合返回:

/**
*    SimpleExecutor类的doQuery()方法实现
*/
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); 
    }
 }
 
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
//对创建的Statement对象设置参数,即设置SQL 语句中 ? 设置为指定的参数
    handler.parameterize(stmt);
    return stmt;
  }

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

/**
*   StatementHandler 类的parameterize(statement) 方法实现 
*/
public void parameterize(Statement statement) throws SQLException {
	//使用ParameterHandler对象来完成对Statement的设值  
    parameterHandler.setParameters((PreparedStatement) statement);
  }
  /*
   *ParameterHandler类的setParameters(PreparedStatement ps) 实现
   * 对某一个Statement进行设置参数
   */
  public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          
          // 每一个Mapping都有一个TypeHandler,根据TypeHandler来对preparedStatement进行设置参数
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
          // 设置参数
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }

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

  /**
   * PreParedStatement类的query方法实现
   */
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
	// 1.调用preparedStatemnt。execute()方法,然后将resultSet交给ResultSetHandler处理  
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //2. 使用ResultHandler来处理ResultSet
    return resultSetHandler.<E> handleResultSets(ps);
  }
/**  
*ResultSetHandler类的handleResultSets()方法实现
*
*/
public List<Object> handleResultSets(Statement stmt) throws SQLException {
    final List<Object> multipleResults = new ArrayList<Object>();
 
    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);
 
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      
      //将resultSet
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
 
    String[] resultSets = mappedStatement.getResulSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
 
    return collapseSingleResultList(multipleResults);
  }
  // DefaultResultSetHandler 类的handleResultSets(Statement stmt)实现 
  //HANDLE RESULT SETS
 
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    final List<Object> multipleResults = new ArrayList<Object>();
 
    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);
 
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      
      //将resultSet
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
 
    String[] resultSets = mappedStatement.getResulSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
 
    return collapseSingleResultList(multipleResults);
  }
 
Executor的功能和作用:

(1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;

(2、为查询创建缓存,以提高性能

(3、创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List查询结果。

StatementHandler:

(0. JDBCPreparedStatement类型的对象,创建的过程,使用包含若干个? 占位符的SQL语句字符串,其后再对占位符进行设值。              

(1.StatementHandler通过parameterize(statement)方法对Statement进行设值;       

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

猜你喜欢

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