1. 程式人生 > >淺談MyBatis的工作原理

淺談MyBatis的工作原理

瞭解MyBatis工作原理先了解這幾個類的作用:

Configuration           MyBatis所有的配置資訊都儲存在Configuration物件之中,配置檔案中的大部分配置都會儲存到該類中

SqlSession               作為MyBatis工作的主要頂層API,表示和資料庫互動時的會話,完成必要資料庫增刪改查功能

Executor                   MyBatis執行器,是MyBatis 排程的核心,負責SQL語句的生成和查詢快取的維護

StatementHandler    封裝了JDBC Statement操作,負責對JDBC statement 的操作,如設定引數等

ParameterHandler    負責對使用者傳遞的引數轉換成JDBC Statement 所對應的資料型別

ResultSetHandler     負責將JDBC返回的ResultSet結果集物件轉換成List型別的集合

TypeHandler             使用Java反射技術完成JavaBean物件到資料庫引數之間的相互轉換

SqlSource                 負責根據使用者傳遞的parameterObject,動態地生成SQL語句,將資訊封裝到BoundSql物件中,並返回

BoundSql                  表示動態生成的SQL語句以及相應的引數資訊

MyBatis層次結構圖:

建立SqlSession的工程原始碼解讀:

// 這是一個建立SqlSession的過程

// 將配置檔案以流的形式引入
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 建立SqlSessionFactory
SqlSessionFactoryBuilder sessionFactory = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = sessionFactory.build(inputStream);
// 建立sqlSession
SqlSession session = factory.openSession();
  • 瞭解一下SqlSessionFactory建立過程及作用:
/**
 * SqlSessionFactoryBuilder類中只有9個重寫的build方法,
 * 我刪除了方法體中的內容、
 */

public class SqlSessionFactoryBuilder {

  
  public SqlSessionFactory build(Reader reader) {}

  public SqlSessionFactory build(Reader reader, String environment) {}

  public SqlSessionFactory build(Reader reader, Properties properties) {}

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {}

  // 以流的形式將配置檔案引入
  public SqlSessionFactory build(InputStream inputStream) {}

  public SqlSessionFactory build(InputStream inputStream, String environment) {}

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {}
  
  // 將主配置檔案載入
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {}
  
  // 將主配置檔案引入的其他配置檔案載入
  public SqlSessionFactory build(Configuration config) {}

}
  • 接下來看一下SqlSession的建立過程:
// SqlSession和核心類Executor掛鉤
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }

  public DefaultSqlSession(Configuration configuration, Executor executor) {
    this(configuration, executor, false);
  }
  • SqlSession都是通過Executor來執行的,所以Executor是MyBatis的核心。
  • Executor並沒有真正的去執行資料庫操作,而是交由StatementHanlder來處理的。
public interface StatementHandler {

	//從Connection中獲取Stament物件
	Statement prepare(Connection connection) throws SQLException;
	
	//設定預處理引數
	void parameterize(Statement statement) throws SQLException;
	
	//呼叫批量操作
	void batch(Statement statement) throws SQLException;
	
	//更新操作
	int update(Statement statement) throws SQLException;
	
	//查詢操作
	<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
	
	//獲取執行SQL語句的封裝類BoundSql
	BoundSql getBoundSql();
	
	//引數處理器
	ParameterHandler getParameterHandler();

}
  •  接下來就是使用ParameterHandler來設定引數,getParameterObject()是獲取引數的,而setParameters()是設定引數的

        ,相當於對一條sql所有的引數都執行ps.setXXX(value); 

/**
 * A parameter handler sets the parameters of the {@code PreparedStatement}
 *
 * @author Clinton Begin
 */
public interface ParameterHandler {

  Object getParameterObject();

  void setParameters(PreparedStatement ps)
      throws SQLException;

}
  • 從mybatis接收引數到mysql儲存資料,都會用到typeHandler型別處理器。這也就是從JavaType->JdbcType的轉化過程。由於mybatis初始時已經內建大部分基礎型別轉化的TypeHandler,已經足夠我們平常的簡單應用開發了,所以大多數情況下並不需要我們自己去定義型別轉換器。
public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    .......
}
  • 這樣就生成了原生的JDBC操作程式碼。
  • 再看ResultSetHandler負責的任務:

(1)處理Statement執行後產生的結果集,生成結果列表

(2)處理儲存過程執行後的輸出引數

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

  // 處理執行statement後的結果集
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  // 執行儲存過程執行後的輸出引數
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}