天天看點

原生Mybatis架構源碼解析

前言:

    mybatis是我們常用的一種操作資料庫的架構。

    我們在使用的mybatis有多種方式:原生mybatis、與Spring結合使用的mybatis、與SprinBoot結合使用的mybatis。

    使用的方式越來越簡單,需要我們配置的項也越來越少,但是原理都是通用的,底層都是mybatis架構,而mybatis架構的底層也就是我們熟悉的JDBC。

    萬變不離其宗,下面請随着筆者來以對照JDBC的方式來剖析mybatis源碼,看完其源碼可以發現其實架構沒有我們想象中的那麼難,隻是封裝的好一點,考慮的情況多一點

1.JDBC操作資料庫的方式

String url = "jdbc:mysql://localhost:3306/test";
		String username = "root";
		String password = "root";
		String sql = "update blog set name = ? where id=?";
		
		try {
			// 1.擷取連接配接
			Connection connection = DriverManager.getConnection(url, username, password);
			
			// 2.建立 preparedStatement
			PreparedStatement prepareStatement = connection.prepareStatement(sql);
			
			// 3.初始化參數
			prepareStatement.setString(1, "lucy");
			prepareStatement.setInt(2, 1);
			
			// 4.執行update
			prepareStatement.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
           

    主要分四步:擷取連接配接、建立preparedStatement、封裝參數、執行

    下面我們也按照這四步來分析Mybatis

2.使用原生mybatis方式來操作資料庫

    關于mybatis的詳細使用讀者可參考 易佰教程 https://www.yiibai.com/mybatis/install_configure.html  

    筆者直接摘錄其中使用mybatis的經典方式,代碼如下(注意:以下代碼來自 易佰教程):

public class HelloWord {
	private static SqlSessionFactory sqlSessionFactory;
	private static Reader reader;

	static {
		try {
            // 1.讀取mybatis配置檔案,并生成SQLSessionFactory
			reader = Resources.getResourceAsReader("config/Configure.xml");
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static SqlSessionFactory getSession() {
		return sqlSessionFactory;
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// 2.擷取session,主要的CRUD操作均在SqlSession中提供
		SqlSession session = sqlSessionFactory.openSession();
		try {
            // 3.執行查詢操作
            // 該方法包括三個步驟:封裝入參數、執行查詢、封裝結果為對象類型
			User user = (User) session.selectOne(
					"com.yiibai.mybatis.models.UserMapper.GetUserByID", 1);
			if(user!=null){
				String userInfo = "名字:"+user.getName()+", 所屬部門:"+user.getDept()+", 首頁:"+user.getWebsite();
				System.out.println(userInfo);
			}
		} finally {
			session.close();
		}
	}

}
           

    通過上面的使用可知,最關鍵的代碼就是session.selectOne(),裡面包括了入參的封裝、查詢的執行、結果封裝。這句話基本對應了JDBC中全部的四步關鍵操作。下面我們先來看下SqlSession的擷取,然後再對其方法進行分析。

3.SqlSession的擷取

// 1.讀取config目錄下Configure.xml檔案
Reader reader = Resources.getResourceAsReader("config/Configure.xml");
// 2.使用SqlSessionFactoryBuilder建立SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 3.在SqlSessionFactory中建立SqlSession
SqlSession session = sqlSessionFactory.openSession();
           

   1)Resources.getResourceAsReader("config/Configure.xml")讀取檔案

public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      // 預設為null,走該步驟
      reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
  }

//getResourceAsStream()
  public static InputStream getResourceAsStream(String resource) throws IOException {
    return getResourceAsStream(null, resource);
  }

//getResourceAsStream()
  public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    // ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) throw new IOException("Could not find resource " + resource);
    return in;
  }

//classLoaderWrapper.getResourceAsStream(resource, loader)
  InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {
      if (null != cl) {

        // 關鍵就是這句話
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) returnValue = cl.getResourceAsStream("/" + resource);

        if (null != returnValue) return returnValue;
      }
    }
    return null;
  }
           

    總結1):主要就是通過ClassLoader.getResourceAsStream()來擷取指定classpath路徑下的Resource

    2)new SqlSessionFactoryBuilder().build(reader)擷取SessionFactory

public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

//build()
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      // 主要就是這句話
      // 實作了兩個功能parse.parse()解析了xml;build(configuration)建立了SqlSessionFactory
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
           

    * 下面看下parse.parse()方法如何進行xml解析

//XMLConfigBuilder.parse()  
public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

// parseConfiguration()
// 可以看到主要是對xml各節點的分析,我們重點關注下mapperElement()方法
  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));// 重點關注下這個方法
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

// mapperElement(root.evalNode("mappers"))
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          // 1.擷取resource資訊,也就是對應的mapper.xml對應路徑
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            // 2.解析對應的mapper.xml檔案,
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            // 3.将解析出來的Mapper對象添加到Configuration中
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
           

    可以看到,通過解析configuration.xml檔案,擷取其中的Environment、Setting,重要的是将<mappers>下的所有<mapper>解析出來之後添加到Configuration,Configuration類似于配置中心,所有的配置資訊都在這裡

    * 解析完成之後,我們來看下parse(configuration)如何生成一個SQLSessionFactory

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
           

    非常簡單,直接把Configuration當做參數,直接new一個DefaultSqlSessionFactory

    3)sqlSessionFactory.openSession()開啟一個SqlSession

public SqlSession openSession() {
    //configuration.getDefaultExecutorType() = ExecutorType.SIMPLE;
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

// openSessionFromDataSource()
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 1.transactionFactory預設為 ManagedTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 2.建立一個Transaction,注意該Transaction是org.apache.ibatis.transaction.Transaction
      // Connection即從此處擷取的
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        
      // 3.建立一個Executor,預設為SimpleExecutor,參考Executor代碼可知,主要的CRUD操作就是再此處完成的
      final Executor executor = configuration.newExecutor(tx, execType);
      // 4.将Executor和Configuration做為參數封裝到DefaultSqlSession,預設傳回該SqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
           

    總結3):該方法中有許多新的類出現,下面我們集中看下這些類,簡單了解下這些類的作用,以便我們更好的了解代碼

    * TransactionFactory

    類似于我們的SessionFactory,主要是用來生産Transaction的,注意這個Transaction是org.apache.ibatis.transaction.Transaction

public interface TransactionFactory {

  void setProperties(Properties props);
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
           

    * org.apache.ibatis.transaction.Transaction

    可以看到getConnection()方法傳回的就是我們夢寐以求的java.sql.Connection,後續的操作都是基于此的

    并且還有一些關于事務的操作commit、rollback等

public interface Transaction {
  // java.sql.Connection
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
}
           

    * Executor

    根據其接口方法可以看到CRUD操作在這裡被實作,看來SqlSession将具體的操作都委托為Executor了

public interface Executor {
...
  int update(MappedStatement ms, Object parameter) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

  <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  Transaction getTransaction();
    ...

}
           

    總結3:

    * 解析configuration.xml檔案,生成對應的Environment、Setting、Mapper,并注冊到Configuration。Configuration相當于配置管理中心,所有的配置都在這裡展現

    * 生成org.apache.ibatis.transaction.Transaction實作類,裡面我們需要的java.sql.Connection

    * 根據Transaction封裝Executor,Executor負責主要的CRUD操作

    * 将Configuration和Executor封裝到SqlSession中,SqlSession對外提供統一操作入口,内部委托為Executor來執行

4.SqlSession.selectOne()方法的執行過程

    SqlSession預設實作為DefaultSqlSession

public <T> T selectOne(String statement, Object parameter) {
    // 直接調用了selectList()方法
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

//selectList()
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      // 1.擷取對應的MappedStatement
      // MappedStatement包裝了每一個CRUD操作對應的詳細資訊
      MappedStatement ms = configuration.getMappedStatement(statement);
        
      // 2.executor預設實作為CachingExecutor
      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();
    }
  }
           

    注意:有關于Cache的筆者就不分析了,網絡上有很多關于Cache的文章,讀者可自行檢視

    可以看到主要的實作都委托給executor了,下面我們重點來看其query()方法的實作

5.CachingExecutor.query()

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

//query()
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    ...
    // 重點就是這句,預設實作在BaseExecutor
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

//BaseExecutor.query()
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ...
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        // 重要實作在這裡
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    ...
    return list;
  }

//queryFromDatabase()
  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 {
      // 主要在這裡,該方法是一個抽象方法,由子類實作,目前項目子類為SimpleExecutor
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    ...
  }
    
//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();
      // 1.StatementHandler封裝了JDBC Statement操作,如設定參數、将Statement結果集轉換成List集合
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 2.封裝preparedStatement的參數(主要功能就在這裡實作)
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 3.執行execute操作
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
           

    總結:分析可知,主要的功能實作在StatementHandler中。

    * StatementHandler建立了PreparedStatement;

    * 封裝了其所需要的參數;

    * 并且對其結果進行處理,封裝為對象

    下面我們逐個方法來看

    1)configuration.newStatementHandler()擷取StatementHandler

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

//RoutingStatementHandler構造方法
  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());
    }

  }
           

    傳回的StatementHandler為PreparedStatementHandler

    2)prepareStatement(handler, ms.getStatementLog())

    功能:擷取Statement;封裝PreparedStatement參數;

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    // 1.擷取java.sql.Connection
    // Connection connection = transaction.getConnection();
    Connection connection = getConnection(statementLog);
    // 2.擷取對應的Statement
    stmt = handler.prepare(connection);
    // 3.封裝參數
    handler.parameterize(stmt);
    return stmt;
  }
           

    * handler.prepare()擷取Statement

    在這裡,最終還是通過java.sql.Connection.prepareStatement(sql)的方式來建立Statement

public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      // 重點在這裡
      statement = instantiateStatement(connection);
      setStatementTimeout(statement);
      setFetchSize(statement);
      return statement;
   ...
  }
   
//PreparedStatementHandler.instantiateStatement()
  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() != null) {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    } else {
      // 采用了預設實作,
      // 直接對應了我們傳統JDBC方式的從connection中擷取PreparedStatement
      return connection.prepareStatement(sql);
    }
  }
           

    * handler.parameterize(stmt)封裝參數

public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

//
  public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 1.parameterMappings包含了需要拼裝的參數
    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;
          // 2.擷取參數名稱
          String propertyName = parameterMapping.getProperty();
          // 3.擷取參數值
          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);
          }
          // 4.擷取參數對應的類型處理器
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
          // 5.對應的封裝參數操作還是要委托給TypeHandler來處理
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        }
      }
    }
  }

//BaseTypeHandler.setParameter()
  public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
    if (parameter == null) {
      if (jdbcType == null) {
        throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
      }
      try {
        ps.setNull(i, jdbcType.TYPE_CODE);
      } catch (SQLException e) {
        throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
        		"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
        		"Cause: " + e, e);
      }
    } else {
      // 由于參數值非空,會走該方法
      setNonNullParameter(ps, i, parameter, jdbcType);
    }
  }
           

    本例中參數類型為Integer,則對應的typeHandler為IntegerTypeHandler,我們可以看到

//  IntegerTypeHandler.setNonNullParameter()
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }
           

    對應于JDBC的處理為preparedStatement.setInt(index,value)

    總結2):進過詳細分析,我們可以看到,最終的處理還是JDBC那一套,通過connection建立preparedStatement;對preparedStatement進行參數封裝;

    下面就是最終執行

    3)handler.<E>query(stmt, resultHandler)執行execute操作

//PreparedStatementHandler.query() 
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 1.進行execute操作
    ps.execute();
    // 2.對結果進行封裝
    return resultSetHandler.<E> handleResultSets(ps);
  }
           

    preparedStatement.execute()這個是正常操作,直接進行執行操作

    最後是對結果進行封裝,交由resultSetHandler來操作

    寫這篇部落格太累了,哈哈,筆者就不打算繼續分析resultSetHandler的操作了,但其核心操作也會與JDBC封裝結果集一緻的

總結:分析下mybatis核心操作如下

    * 解析configuration.xml,生成Environment、Setting、Mapper等對象,并注冊到Configuration

    * 從SqlSessionFactory中擷取SqlSession,SqlSession作為操作入口,接收使用者的資料庫操作,并委托給内部的Executor來實作

    * Executor内部StatementHandler負責Statement的建立;PreparedStatement參數的注入;execute方法的執行,是以,可以說重要的執行操作都在StatementHandler中

    * execute方法執行過會,ResultSetHandler進行結果的封裝

推薦一篇很不錯的部落格 https://blog.csdn.net/luanlouis/article/details/40422941 

以下内容來自該部落格

Mybatis的核心部件:

SqlSession            作為MyBatis工作的主要頂層API,表示和資料庫互動的會話,完成必要資料庫增删改查功能
Executor              MyBatis執行器,是MyBatis 排程的核心,負責SQL語句的生成和查詢緩存的維護
StatementHandler   封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設定參數、将Statement結果集轉換成List集合。
ParameterHandler   負責對使用者傳遞的參數轉換成JDBC Statement 所需要的參數,
ResultSetHandler    負責将JDBC傳回的ResultSet結果集對象轉換成List類型的集合;
TypeHandler          負責java資料類型和jdbc資料類型之間的映射和轉換
MappedStatement   MappedStatement維護了一條<select|update|delete|insert>節點的封裝, 
SqlSource            負責根據使用者傳遞的parameterObject,動态地生成SQL語句,将資訊封裝到BoundSql對象中,并傳回
BoundSql             表示動态生成的SQL語句以及相應的參數資訊
Configuration        MyBatis所有的配置資訊都維持在Configuration對象之中。
           

繼續閱讀