ITPub博客

首页 > > > MyBatis的架构设计以及实例分析--转-2

MyBatis的架构设计以及实例分析--转-2

作者:zhmw 时间:2020-02-04 11:58:54 0 删除 编辑

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

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. 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()方法的定义: 

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. public <E> List<E> selectList(String statement, Object parameter) {  
  2.   return this.selectList(statement, parameter, RowBounds.DEFAULT);  
  3. }  
  4.   
  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的配置文件中配置信息的组织格式和内存中对象的组织格式几乎完全对应的。上述例子中的

 

 

[html] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  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" ,value为MappedStatement对象的形式维护到Configuration的一个Map中。当以后需要使用的时候,只需要通过Id值来获取就可以了。

从上述的代码中我们可以看到SqlSession的职能是:

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

 

3.MyBatis执行器Executor根据SqlSession传递的参数执行query()方法(由于代码过长,读者只需阅读我注释的地方即可):

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. /** 
  2. * BaseExecutor 类部分代码 
  3. * 
  4. */  
  5. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {  
  6.         
  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.  }  
  13.   
  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 {  
  49.           
  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.   }  

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  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() 方法的实现:

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  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); } }  
  7.   
  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. 对于JDBC的PreparedStatement类型的对象,创建的过程中,我们使用的是SQL语句字符串会包含 若干个? 占位符,我们其后再对占位符进行设值。

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

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

 

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

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. /** 
  2. *   StatementHandler 类的parameterize(statement) 方法实现  
  3. */  
  4. public void parameterize(Statement statement) throws SQLException {  
  5.     //使用ParameterHandler对象来完成对Statement的设值    
  6.     parameterHandler.setParameters((PreparedStatement) statement);  
  7.   }  

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  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.         }  
  25.           
  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)方法的实现:

 

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  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.  }  
[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. /**   
  2. *ResultSetHandler类的handleResultSets()方法实现 
  3. * 
  4. */  
  5. public List<Object> handleResultSets(Statement stmt) throws SQLException {  
  6.     final List<Object> multipleResults = new ArrayList<Object>();  
  7.   
  8.     int resultSetCount = 0;  
  9.     ResultSetWrapper rsw = getFirstResultSet(stmt);  
  10.   
  11.     List<ResultMap> resultMaps = mappedStatement.getResultMaps();  
  12.     int resultMapCount = resultMaps.size();  
  13.     validateResultMapsCount(rsw, resultMapCount);  
  14.       
  15.     while (rsw != null && resultMapCount > resultSetCount) {  
  16.       ResultMap resultMap = resultMaps.get(resultSetCount);  
  17.         
  18.       //将resultSet  
  19.       handleResultSet(rsw, resultMap, multipleResults, null);  
  20.       rsw = getNextResultSet(stmt);  
  21.       cleanUpAfterHandlingResultSet();  
  22.       resultSetCount++;  
  23.     }  
  24.   
  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.     }  
  39.   
  40.     return collapseSingleResultList(multipleResults);  
  41.   }  

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

 

[java] view plain copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. //  
  2. // DefaultResultSetHandler 类的handleResultSets(Statement stmt)实现   
  3. //HANDLE RESULT SETS  
  4. //  
  5.   
  6. public List<Object> handleResultSets(Statement stmt) throws SQLException {  
  7.   final List<Object> multipleResults = new ArrayList<Object>();  
  8.   
  9.   int resultSetCount = 0;  
  10.   ResultSetWrapper rsw = getFirstResultSet(stmt);  
  11.   
  12.   List<ResultMap> resultMaps = mappedStatement.getResultMaps();  
  13.   int resultMapCount = resultMaps.size();  
  14.   validateResultMapsCount(rsw, resultMapCount);  
  15.     
  16.   while (rsw != null && resultMapCount > resultSetCount) {  
  17.     ResultMap resultMap = resultMaps.get(resultSetCount);  
  18.       
  19.     //将resultSet  
  20.     handleResultSet(rsw, resultMap, multipleResults, null);  
  21.     rsw = getNextResultSet(stmt);  
  22.     cleanUpAfterHandlingResultSet();  
  23.     resultSetCount++;  
  24.   }  
  25.   
  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.   }  
  40.   
  41.   return collapseSingleResultList(multipleResults);  
  42. }  

 

 

由于上述的过程时序图太过复杂,就不贴出来了,读者可以下载MyBatis源码, 使用Eclipse、Intellij IDEA、NetBeans 等IDE集成环境创建项目,Debug MyBatis源码,一步步跟踪MyBatis的实现,这样对学习MyBatis框架很有帮助~

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69901620/viewspace-3138419/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-12-04

  • 博文量
    295
  • 访问量
    9529