iBATIS 对 SQL 语句的解析过程

总体来说 iBATIS 的系统结构还是比较简单的,它主要完成两件事情:
根据 JDBC 规范建立与数据库的连接;
通过反射打通 Java 对象与数据库参数交互之间相互转化关系。
iBATIS 的框架结构也是按照这种思想来组织类层次结构的,其实它是一种典型的交互式框架。先期准备好交互的必要条件,然后构建一个交互的环境,交互环境中还划分成会话,每次的会话也有一个环境。当这些环境都准备好了以后,剩下的就是交换数据了。其实涉及到网络通信,一般都会是类似的处理方式。
下图是 iBATIS 框架的主要的类层次结构图


上面的类图中左边 SqlMapClient 接口主要定义了客户端的操作行为包括 select、insert、update、delete。而右边主要是定义了当前客户端在当前线程的执行环境。SqlMapSession 可以共享使用,也可以自己创建,如果是自己创建在结束时必须要调用关闭接口关闭。
当使用者持有了 SqlMapClientImpl 对象就可以使用 iBATIS 来工作了。这里还要提到另外一个类 SqlMapExecutorDelegate 这个类从名字就可以看出他是执行代理类。这个类非常重要,重要是因为他耦合了用户端的执行操作行为和执行的环境,他持有执行操作的所需要的数据,同时提供管理着执行操作依赖的环境。所以他是一个强耦合的类,也可以看做是个工具类。

核心接口
ibatis抽取了以下几个重要接口:

1. SqlMapExecutor
该接口是对SQL操作行为的抽象,提供了SQL单条执行和批处理涉及的所有操作方法。




2. SqlMapTransactionManager
该接口是对事务行为的抽象,提供了事务执行过程中的涉及的所有方法



3. SqlMapClient
该接口定位是SQL执行客户端,是线程安全的,用于处理多个线程的sql执行。它继承了上面两个接口,这意味着该接口具有SQL执行、批处理和事务处理的能力,如果你拥有该接口的实现类,意味着执行任何SQL语句对你来说是小菜一碟。该接口的核心实现类是SqlMapClientImpl。

4. SqlMapSession
该接口在继承关系上和SqlMapClient一致,但它的定位是保存单线程sql执行过程的session信息。该接口的核心实现类是SqlMapSessionImpl。

5. MappedStatement
该接口定位是单条SQL执行时的上下文环境信息,如SQL标识、SQL、参数信息、返回结果、操作行为等。

6. ParameterMap/ResultMap
该接口用于在SQL执行的前后提供参数准备和执行结果集的处理。

这里必须要强调SqlMapExecutorDelegate这个类,他是一个执行代理类,在ibatis框架中地位非常重要,因为他耦合了用户端的操作行为和执行环境,他持有执行操作的所需要的所有数据,同时管理着执行操作依赖的环境。

下面是一个Spring中sqlMapClient的bean配置:

	<bean id="group1SqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
		<property name="configLocation">
			<value>sql-map.xml</value>
		</property>
		<property name="dataSource">
			<ref local="group1"/>
		</property>
	</bean>


下面看一下SqlMapClientFactoryBean的初始化方法afterPropertiesSet(),用于构建sqlMapClient对象:

public void afterPropertiesSet() throws Exception {  
    ...  
  
    this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties);   //初始化核心方法,构建sqlMapClient对象  
  
    ...  
}  


看一下buildSqlMapClient()的实现:

protected SqlMapClient buildSqlMapClient(  
        Resource[] configLocations, Resource[] mappingLocations, Properties properties)  
        throws IOException {  
               ...  
    SqlMapClient client = null;  
    SqlMapConfigParser configParser = new SqlMapConfigParser();  
    for (int i = 0; i < configLocations.length; i++) {  
        InputStream is = configLocations[i].getInputStream();  
        try {  
            client = configParser.parse(is, properties); //通过SqlMapConfigParser解析配置文件,生成SQLMapClientImpl对象  
        }  
        catch (RuntimeException ex) {  
            throw new NestedIOException("Failed to parse config resource: " + configLocations[i], ex.getCause());  
        }  
    }  
    ...  
    return client;  
}  


SQL执行过程
下面以一个select语句的执行过程,分析一下以上各ibatis核心接口相互如何配合。
1. dao调用SqlMapClientTemplate的query()方法:
public Object queryForObject(final String statementName, final Object parameterObject)  
        throws DataAccessException {  
  
    return execute(new SqlMapClientCallback() {  
        public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {  
            return executor.queryForObject(statementName, parameterObject);  
        }  
    });  
}  


2. execute()方法中展示了执行过程中的核心逻辑:获取session -> 获取connection -> 执行sql ->释放connection -> 关闭session。
部分源码如下:
public Object execute(SqlMapClientCallback action) throws DataAccessException {  
                ...  
        SqlMapSession session = this.sqlMapClient.openSession(); //获取session信息  
          
        Connection ibatisCon = null;     //获取Connection  
  
        try {  
            Connection springCon = null;  
            DataSource dataSource = getDataSource();  
            boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);  
  
            try {  
                ibatisCon = session.getCurrentConnection();  
                if (ibatisCon == null) {  
                    springCon = (transactionAware ?  
                            dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));  
                    session.setUserConnection(springCon);  
                ...  
                }  
            }  
            ...  
  
            // Execute given callback...  
            try {  
                return action.doInSqlMapClient(session); //执行SQL  
            }  
            ...  
            finally {  
                        // 关闭Connection  
                try {  
                    if (springCon != null) {  
                        if (transactionAware) {  
                            springCon.close();   
                        }  
                        else {  
                            DataSourceUtils.doReleaseConnection(springCon, dataSource);  
                        }  
                    }  
                }  
                  
            if (ibatisCon == null) {  
                session.close(); //关闭session  
            }  
        }  


3. action.doInSqlMapClient(session)调用SqlMapSessionImpl().queryForObject()方法。
注意: 这里调用对象主体为SqlMapSessionImpl,表示在线程session中执行sql(session是ThreadLocal的),而不在负责整体协调的SqlMapClientImpl中执行sql语句。
源码如下:
public Object queryForObject(final String statementName, final Object parameterObject)  
        throws DataAccessException {  
  
    return execute(new SqlMapClientCallback() {  
       //这里的SqlMapExecutor对象类型为SqlMapSessionImpl  
        public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {  
            return executor.queryForObject(statementName, parameterObject);  
        }  
    });  
}  


4. SqlMapSessionImpl().queryForObject()的方法很简单,直接交给代理对象SqlMapExecutorDelegate处理:

public Object queryForObject(String id, Object paramObject) throws SQLException {  
  return delegate.queryForObject(session, id, paramObject);  
}  


5. SqlMapExecutorDelegate的queryForObject()方法代码如下:
public Object queryForObject(SessionScope session, String id, Object paramObject, Object resultObject) throws SQLException {  
  Object object = null;  
  
  MappedStatement ms = getMappedStatement(id); //MappedStatement对象集是上文中提及的初始化方法SqlMapClientFactoryBean.afterPropertiesSet()中,由配置文件构建而成  
  Transaction trans = getTransaction(session); // 用于事务执行  
  boolean autoStart = trans == null;  
  
  try {  
    trans = autoStartTransaction(session, autoStart, trans);  
  
    RequestScope request = popRequest(session, ms);  // 从RequestScope池中获取该次sql执行中的上下文环境RequestScope   
    try {  
      object = ms.executeQueryForObject(request, trans, paramObject, resultObject);   // 执行sql  
    } finally {  
      pushRequest(request);  //归还RequestScope  
    }  
  
    autoCommitTransaction(session, autoStart);  
  } finally {  
    autoEndTransaction(session, autoStart);  
  }  
  
  return object;  
}  

6. MappedStatement携带了SQL语句和执行过程中的相关信息,MappedStatement.executeQueryForObject()方法部分源码如下:
public Object executeQueryForObject(RequestScope request, Transaction trans, Object parameterObject, Object resultObject)  
    throws SQLException {  
  try {  
    Object object = null;  
  
    DefaultRowHandler rowHandler = new DefaultRowHandler();  
    executeQueryWithCallback(request, trans.getConnection(), parameterObject, resultObject, rowHandler, SqlExecutor.NO_SKIPPED_RESULTS, SqlExecutor.NO_MAXIMUM_RESULTS); //执行sql语句  
      
    //结果处理,返回结果  
    List list = rowHandler.getList();   
    if (list.size() > 1) {  
      throw new SQLException("Error: executeQueryForObject returned too many results.");  
    } else if (list.size() > 0) {  
      object = list.get(0);  
    }  
  
    return object;  
  }   
  ....  
} 


7. MappedStatement.executeQueryWithCallback()方法包含了参数值映射、sql准备和sql执行等关键过程,部分源码如下:
protected void executeQueryWithCallback(RequestScope request, Connection conn, Object parameterObject, Object resultObject, RowHandler rowHandler, int skipResults, int maxResults)  
    throws SQLException {  
    ...  
  try {  
    parameterObject = validateParameter(parameterObject);  //验证入参  
  
    Sql sql = getSql();  //获取SQL对象  
  
    errorContext.setMoreInfo("Check the parameter map.");  
    ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);// 入参映射  
  
    errorContext.setMoreInfo("Check the result map.");  
    ResultMap resultMap = sql.getResultMap(request, parameterObject); //结果映射  
  
    request.setResultMap(resultMap);  
    request.setParameterMap(parameterMap);  
  
    errorContext.setMoreInfo("Check the parameter map.");  
    Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject);  //获取参数值  
  
    errorContext.setMoreInfo("Check the SQL statement.");  
    String sqlString = sql.getSql(request, parameterObject);  //获取拼装后的sql语句  
  
    errorContext.setActivity("executing mapped statement");  
    errorContext.setMoreInfo("Check the SQL statement or the result map.");  
    RowHandlerCallback callback = new RowHandlerCallback(resultMap, resultObject, rowHandler);  
    sqlExecuteQuery(request, conn, sqlString, parameters, skipResults, maxResults, callback);  //sql执行  
  
    errorContext.setMoreInfo("Check the output parameters.");  
    if (parameterObject != null) {  
      postProcessParameterObject(request, parameterObject, parameters);  
    }  
  
    errorContext.reset();  
    sql.cleanup(request);  
    notifyListeners();  
    ....  
}  


8. 到了执行中最核心的一步,也是最后一步: MappedStatement.sqlExecuteQuery()方法,它负责sql的最后执行,内部调用了SqlExecutor.executeQuery()方法,部分源码如下:
public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {  
  ...  
  PreparedStatement ps = null;  
  ResultSet rs = null;  
  setupResultObjectFactory(request);  
  try {  
    errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");  
    Integer rsType = request.getStatement().getResultSetType();  
    //初始化PreparedStatement,设置sql、参数值等  
    if (rsType != null) {  
      ps = prepareStatement(request.getSession(), conn, sql, rsType);  
    } else {  
      ps = prepareStatement(request.getSession(), conn, sql);  
    }  
    setStatementTimeout(request.getStatement(), ps);  
    Integer fetchSize = request.getStatement().getFetchSize();  
    if (fetchSize != null) {  
      ps.setFetchSize(fetchSize.intValue());  
    }  
    errorContext.setMoreInfo("Check the parameters (set parameters failed).");  
    request.getParameterMap().setParameters(request, ps, parameters);  
    errorContext.setMoreInfo("Check the statement (query failed).");  
    ps.execute(); //执行  
    errorContext.setMoreInfo("Check the results (failed to retrieve results).");  
  
    // ResultSet处理  
    rs = handleMultipleResults(ps, request, skipResults, maxResults, callback);  
  } finally {  
    try {  
      closeResultSet(rs);  
    } finally {  
      closeStatement(request.getSession(), ps);  
    }  
  }  
  
}  

上面这段代码大家会非常熟悉,和本文开始处的代码很相似,ibatis归根到底,是对JDBC操作一定程度上的封装而已。

下面在总体上概括sql的一般执行过程:
SqlMapClientImpl接到请求后,创建SqlMapSessionImpl对象(ThreadLocal,保证线程安全),SqlMapSessionImpl交由内部的代理类SqlMapExecutorDelegate执行,代理类获取相应的MappedStatement,交由MappedStatement对象执行,MappedStatement交由SqlExecutor执行,最终使用JDBC方式执行sql。

小结
ibatis源码规模较小,整体设计思路清晰,阅读ibatis源码可以按以下思路进行:
1. 了解ibatis框架的整体目标,用于解决哪些问题。
2. ibatis如何解决这些问题,带着问题去学习。
3. 了解ibatis框架的核心接口和整体设计思路。
4. 抓住ibatis核心流程: 初始化和请求处理流程。
5. 详细ibatis框架的关键细节实现,如ibatis中的配置文件解析,参数和结果映射等。

猜你喜欢

转载自yinny.iteye.com/blog/1897168