淺談MyBatis的工作原理
阿新 • • 發佈:2019-01-25
瞭解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;
}