02-MyBatisののプロセス分析SQLの実行



このブログはMyBatisのSQL実行プロセスに焦点を当て、および実装プロセスでのキャッシュについては、動的SQLの生成およびその他の詳細は、対応するコンテンツの分析、それだけでブログを書き戻し、このブログには反映されません。

または列子として前のクエリで:

public class UserDaoTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws Exception{
        ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
        InputStream inputStream = resource.getInputStream();
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void selectUserTest(){
        String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);
        Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);
        System.out.println(cbondissuer);
        sqlSession.close();
    }

}

前述した様々なCRUD操作は、get SQLSESSION後に行うことができ、我々はsqlSession.getMapperにこのアプローチを分析するために始めたので、全体の実行プロセスでのSQL表情は優しい方法です。

取得マッパー

sqlSession.getMapper方法を入力し、あなたは曲がgetMapperメソッド構成ガイドオブジェクトがわかります。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    //mapperRegistry实质上是一个Map,里面注册了启动过程中解析的各种Mapper.xml
    //mapperRegistry的key是接口的全限定名,比如com.csx.demo.spring.boot.dao.SysUserMapper
    //mapperRegistry的Value是MapperProxyFactory,用于生成对应的MapperProxy(动态代理类)
    return mapperRegistry.getMapper(type, sqlSession);
}

getMapper方法を入力します。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    //如果配置文件中没有配置相关Mapper,直接抛异常
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      //关键方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

入るMapperProxyFactoryのNewInstanceメソッド:

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;
  }

  //生成Mapper接口的动态代理类MapperProxy
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> 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);
  }

}

以下はMyBatisのマッパーインターフェースは、プロキシオブジェクトクラスを委託しないMapperProxyように、クラスに達成していないため、動的プロキシクラスMapperProxyは、プロキシクラス(ノートを呼び出すために、最初のメソッドを呼び出すすべてのメソッドインターフェイスマッパーを呼び出すあるMapperProxy乾燥プロキシクラスとデリゲートそういったこと)。さて、次の方法の焦点たinvoke見えます。

//MapperProxy代理类
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 {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //获取MapperMethod,并调用MapperMethod
    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;
  }

  @UsesJava7
  private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
      throws Throwable {
    final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
    if (!constructor.isAccessible()) {
      constructor.setAccessible(true);
    }
    final Class<?> declaringClass = method.getDeclaringClass();
    return constructor
        .newInstance(declaringClass,
            MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
  }

  /**
   * Backport of java.lang.reflect.Method#isDefault()
   */
  private boolean isDefaultMethod(Method method) {
    return ((method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
        && method.getDeclaringClass().isInterface();
  }
}

だからここにあなたがのexecuteメソッドのMapperMethodを入力する必要があります。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //判断是CRUD那种方法
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

次に、呼び出すことによって層ごとに、最終的にはdoQueryメソッドに来て、ここでは簡単にExcutor doQueryアプローチを見つけるために、実装を見て、私はここでSimpleExecutorを選びました:

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();
      //内部封装了ParameterHandler和ResultSetHandler
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler封装了Statement, 让 StatementHandler 去处理
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

次に、聞かせてのは、そのため、それに対処する方法を見てみましょう(これは私たちが最も一般的に使用され、パッケージされたPreparedStatementがある)StatementHandler見に実装クラスPreparedStatementHandlerを取ります:

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
     //到此,原形毕露, PreparedStatement, 这个大家都已经滚瓜烂熟了吧
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    //结果交给了ResultSetHandler 去处理,处理完之后返回给客户端
    return resultSetHandler.<E> handleResultSets(ps);
  }

これは、全体のコール処理は終了します。

簡単な概要

流れと一緒にここに来るSQLSESSION、簡単な要約を行います。

  • などの属性の設定、エイリアス設定、インターセプタ構成、環境(データソースおよびトランザクションマネージャ)、マッパーの構成を含むSqlSessionFactoryBuilder分析プロファイルは、;コンフィギュレーションを解析した後、必要なすべてのMyBatisのが含まれている構成ガイドオブジェクトを生成します。構成は、次にSqlSessionFactory構成ガイドオブジェクトを含むオブジェクトを使用してこの構成ガイドオブジェクトを作成します。
  • SqlSessionFactoryを取得した後は、SQLアクチュエータを作成openSesison方法、のSqlSessionFactoryが(エグゼキュータコンポーネントは、トランザクションオブジェクトを含む)、呼び出し、エージェントはあなたが設定したSQLインターセプタメソッドを実行します
  • SQLのアクチュエータの上に取得した後、SQLSESSION(使用DefaultSqlSessionデフォルト)を作成し、このSQLSESSIONもエグゼキュータは、オブジェクトと上記の構成ガイドを作成したオブジェクトを、それがSQLSESSIONグローバルコンフィギュレーションを介して取得することができます含まれています。
  • あなたはSQLSESSIONオブジェクトを取得した後のCRUDの様々な方法を実行することができるようになります。

これらは、SQLSESSIONプロセス、SQLは次の要約で説明このブログの実装プロセスを得ています。

  • getMapperの方法の呼び出しSQLSESSION、MapperProxyインタフェース動的プロキシオブジェクトマッパー、メソッド呼び出しMapperProxyにインタフェースメソッド呼び出し(動的プロキシ機構)マッパーであろうすべてのコール。
  • 方法MapperProxyたinvoke行うための唯一の方法は、SQLSESSIONが上院にメソッドとして実行されます、MapperMethodオブジェクトを作成し、メソッドを実行し、このオブジェクトを呼び出すことです。
  • ダウンは、変調層下方アセンブリエグゼキュータに入る(動的構成ウィジェットが執行エージェントする場合)クエリメソッドは、この方法はParameterHandler ResultSetHandlerとオブジェクトの両方をカプセル化するStatementHandlerオブジェクトを作成します。SQLに設定されたパラメータParameterHandlerを使用して、コンパイラのパラメータおよびパラメータ値StatementHandlerプリセットを呼び出します。

エグゼキュータコンポーネントは、2つの直接実装クラスを持っており、BaseExecutor CachingExecutorです。CachingExecutor静的エージェントBaseExecutor。エグゼキュータ成分封入のtransctionアセンブリ、のtransctionアセンブリ分配アセンブリデータソースを有しています。

  • 結果を得るためのコールStatementHandler CRUDメソッド、ResultSetHandlerはカプセル化変換要求が終了し結果。

四つの成分上記キュータ、StatementHandler、ParameterHandler、ResultSetHandler、MyBatisのプラグは、動的エージェントであろう。

重要なクラス

  • MapperProxyFactory

  • MapperProxy

  • フォルダの方法

  • SQLSESSION:メイントップレベルのAPI MyBatisの仕事、プレゼンテーションおよびセッションのデータベースとの対話など、必要に応じてデータベースのCRUD機能を完了するために、

  • エグゼキュータ:MyBatisのアクチュエータは、MyBatisのスケジューリングの中核である、SQL文とクエリキャッシュのメンテナンスを生成するための責任を負います。

    StatementHandler JDBCステートメントは、ステートメントリストコレクションに結果セットを変換し、このようなパラメータを設定すると、操作はJDBC文を担当して、操作をカプセル化します。
    ParameterHandlerユーザがパラメータにパラメータを渡すための責任である、JDBCステートメントを必要
    JDBC ResultSetを変換するResultSetHandler責任リストタイプのセットに結果セットオブジェクトを返した。
    JavaのJDBCタイプとデータ型の間のマッピングおよびデータ変換を担うTypeHandler
    MappedStatement MappedStatement <|挿入更新| |選択削除維持 > パッケージノードを、
    SqlSource責任記載parameterObjectユーザが動的送信BoundSqlオブジェクト戻るにSQL文、カプセル化情報を生成する
    BoundSqlは、動的に生成されたSQL文を表現し、対応しますパラメータ情報

    すべてのコンフィギュレーションMyBatisの構成情報は、Configurationオブジェクトに維持されています。

参照

  • https://www.cnblogs.com/dongying/p/4031382.html
  • https://blog.csdn.net/qq_38409944/article/details/82494187

おすすめ

転載: www.cnblogs.com/54chensongxia/p/11850470.html