1. 程式人生 > >一起學習Mybatis----mybatis的執行原理

一起學習Mybatis----mybatis的執行原理

mybatis的生命週期包含4部分:

         1. SqlSessionFactoryBuilder

          2.SqlSessionFactory

          3.SqlSession

          4.Mapper

      mybatis的執行是先根據相關的配置檔案通過SqlSessionFactoryBuilder構建SqlSessionFactory,在根據SqlSessionFactory去建立響應的SqlSession,SqlSession在根據Mapper傳遞的sql,進行相關的動作,返回結構給Mapper.

構建SqlSessionFactory過程:

    第一步:
         通過 org.apache. ibatis. builder.xmL.XMLConfigBuilder解析配置的XML檔案,讀出配置引數,並將讀取的資料存入這個org.apache ibatis session. Configuration類中。注意MyBatis幾乎所有的配置都是存在這裡的。
    第二步:
         使用 Confinguration物件去建立 SqlSession Factory。 My Batis中的 SqlSession Factory是一個介面,而不是實現類,為此 My Batis提供了一個預設的 Sqlsession Factory實現類,我們般都會使用它 org.apache ibatis session. defaults. Defaultsqlsession Factory。注意,在大部分情況下我們都沒有必要自己去建立新的 Sqlsession Factory的實現類。

Configuration 原始碼:

public class Configuration {
    protected Environment environment;
    protected boolean safeRowBoundsEnabled;
    protected boolean safeResultHandlerEnabled;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel;
    protected boolean cacheEnabled;
    protected boolean callSettersOnNulls;
    protected boolean useActualParamName;
    protected boolean returnInstanceForEmptyRow;
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected LocalCacheScope localCacheScope;
    protected JdbcType jdbcTypeForNull;
    protected Set<String> lazyLoadTriggerMethods;
    protected Integer defaultStatementTimeout;
    protected Integer defaultFetchSize;
    protected ExecutorType defaultExecutorType;
    protected AutoMappingBehavior autoMappingBehavior;
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;
    protected Properties variables;
    protected ReflectorFactory reflectorFactory;
    protected ObjectFactory objectFactory;
    protected ObjectWrapperFactory objectWrapperFactory;
    protected boolean lazyLoadingEnabled;
    protected ProxyFactory proxyFactory;
。。。。。。

   Configuration作用:

     1. 讀入配置檔案,包括基礎配置的XML檔案和對映器的XML檔案。
     2.初始化基礎配置,比如 My Batis的別名等,一些重要的類物件,例如,外掛、對映器、 ObjectFactory和 type Handler物件
     3.提供單例,為後續建立 SessionFactory服務並提供配置的引數執行一些重要的物件方法,初始化配置資訊。

   在第一步中Mybatis會讀取所有的xml檔案,將其初始化儲存到 Configuration類的單例物件中,初始化包含了:properties全域性引數settings設定typeAliases別名typeHandler型別處理器ObjectFactory物件plugin外掛,environment環境,DatabaseldProvider資料庫標識Mapper對映器。 

     

  sqlSessionFactory=new SqlSessionFactoryBuilder(). build(inputstream).

 org.apache.ibatis.session.SqlSessionFactoryBuilder中的builder方法。

  
 MyBatis會根據 Configuration的配置讀取所配置的資訊,構建 SqlsessionFactory物件。

SqLSession執行過程:

  探究其原理的瞭解一下 Mapper對映原理。

 Mapper對映器:構建需要執行的Sql指令碼。由三部分組成:

      1. MappedStatement,它儲存對映器的一個節點( select|insert|delete|update)。包括許多我們配置的SQL、SQL的id、快取資訊、 resultMap、 parameterType、 resultType、languageDriver等重要配置內容。
      2.SqLSource,它是提供 BoundS物件的地方,它是 MappedStatement的一個屬性。
      3. BoundSql,它是建立SQL和引數的地方。它有3個常用的屬性:SQL、 parameterObject ,parameterMappings。

 Mapper對映是通過動態代理實現的。

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.binding;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.ibatis.session.SqlSession;

/**
 * @author Lasse Voss
 */
public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
   //動態代理對Mapper介面實行了代理模式,而代理的方法都放置到了MapperProxy類中了
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}
package org.apache.ibatis.binding;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;

/**
  動態代理模式
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //判斷它是否是一個類,顯然這裡 Mapper是一個介面不是類,所以判定失敗
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

   上面運用了 invoke方法。一旦 mapper是一個代理物件,那麼它就會執行到 invoke方法裡面, invoke首先判斷它是否是一個類,顯然這裡 Mapper是一個介面不是類,所以判定失敗。那麼就會生成 MapperMethod物件,它是通過 cachedMapperMethod方法對其初始化的,然後執行 execute方法,把 sqlsession和當前執行的引數傳遞進去。

      Mapper執行的過程是通過 Executor、 StatementHandler、Parameter Handler和 Resulthandler來完成資料庫操作和結果返回的。

       Executor代表執行器,由它來排程 Statementhandler、 ParameterHandler、 Resulthandler等來執行對應的SQL。

      StatementHandler的作用是使用資料庫的 Statement( PreparedStatement)執行操作,它是四大物件的核心,起到承上啟下的作用。

     ParameterHandler用於SQL對引數的處理。

    Resulthandler是進行最後資料集( Resultset)的封裝返回處理的。

Executor執行器:建立執行器首先根據Configuration獲取執行器型別。

 在建立sqlSession就開始初始化:

生成指定的執行器。

public class SimpleExecutor extends BaseExecutor {

  。。。。。。。。。。。。。。。。。。。。。。。。。。。。

  @Override
  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);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

 。。。。。。。。。。。。。。。。。。。。。。。。。。

}

    顯然 MyBatis根據 Configuration來構建 Statementhandler,然後使用 prepareStatement方法,對SQL編譯並對引數進行初始化,我們在看它的實現過程,它呼叫了 Statementhandler的prepare()進行了預編譯和基礎設定,然後通過 StatementHandler的 parameterize()來設定引數並執行, resulthandler再組裝査詢結果返回給呼叫者來完成一次查詢。

資料庫會話器 StatementHandler:專門處理資料庫會話的。

建立會話器:

 

   RoutingStatementHandler不是我們真實的服務物件,它是通過適配模式找到對應的StatementHandler來執行的。在MyBatis中, StatementHandler和 Executor一樣分為三種SimpleStatementHandler、PreparedStatementHandler、CallableStatementHandler它所對應的是三種執行器。

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

我們最常用的是PreparedStatementHandler:

 預編譯

預編譯完成就需要開始設定引數。

ParameterHandler:

 設定完引數就需要開始查詢了,進行結構封裝。

Resulthandler:

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.resultset;

import org.apache.ibatis.cursor.Cursor;

import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * @author Clinton Begin
 */
public interface ResultSetHandler {

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

實現:

結果封裝返回。

SqlSession執行原理圖: