mybatis在執行期間,主要有四大核心接口對象:
- 執行器Executor,執行器負責整個SQL執行過程的總體控制。
- 語句處理器StatementHandler,語句處理器負責和JDBC層具體交互,包括prepare語句,執行語句,以及調用ParameterHandler.parameterize()設置參數。
- 參數處理器ParameterHandler,參數處理器負責PreparedStatement入參的具體設置。
- 結果集處理器ResultSetHandler,結果處理器負責將JDBC查詢結果映射到java對象。
1、執行器Executor
所有我們在應用層通過sqlSession執行的各類selectXXX和增刪改操作在做了動態sql和參數相關的封裝處理後,都被委託給具體的執行器去執行,包括一、二級緩存的管理,事務的具體管理。執行器比較像是sqlSession下的各個策略工廠實現,用戶通過配置決定使用哪個策略工廠。
mybatis提供了兩種類型的執行器,緩存執行器(CachingExecutor)與非緩存執行器(SimpleExecutor、ReuseExecutor、BatchExecuto。默認是SIMPLE),是否使用緩存執行器則是通過執行cacheEnabled控制的,默認是true。
緩存執行器不是真正功能上獨立的執行器,而是非緩存執行器的裝飾器模式。我們先來看非緩存執行器。非緩存執行器又分爲三種,這三種類型的執行器都基於基礎執行器BaseExecutor,基礎執行器完成了大部分的公共功能
Mybatis有三種基本的Executor執行器:
- SimpleExecutor:每執行一次update或query,就開啓一個Statement對象,用完立刻關閉Statement對象。注:增刪改爲update,查詢爲query。
- ReuseExecutor:執行update或query,以sql作爲key查找Statement對象,存在就使用,不存在就創建,用完後,不關閉Statement對象,而是放置於Map內,供下一次使用。簡言之,就是重複使用Statement對象。
- BatchExecutor:執行update(沒有query,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它緩存了多個Statement對象,每個Statement對象都是addBatch()完畢後,等待逐一執行executeBatch()批處理。與JDBC批處理相同。作用範圍:Executor的這些特點,都嚴格限制在SqlSession生命週期範圍內。
Mybatis中如何指定使用哪一種Executor執行器?
1、在Mybatis配置文件中,可以指定默認的ExecutorType執行器類型,<setting name="defaultExecutorType" value="SIMPLE"/>
2、手動給DefaultSqlSessionFactory的創建SqlSession的方法傳遞ExecutorType類型參數。SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit)
2、StatementHandler:
mybatis包含了三種類型的StatementHandler實現,分別用於JDBC對應的PrepareStatement,Statement以及CallableStatement。BaseStatementHandler是這三種類型語句處理器的抽象父類,封裝了一些實現細節比如設置超時時間、結果集每次提取大小等操作。
3、ParameterHandler
ParameterHandler的接口定義如下:
public interface ParameterHandler {
Object getParameterObject();
void setParameters(PreparedStatement ps);
}
ParameterHandler只有一個默認實現DefaultParameterHandler.
4、ResultSetHandler
結果集處理器,顧名知義,就是用了對查詢結果集進行處理的,目標是將JDBC結果集映射爲業務對象。其接口定義如下:
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;
}
接口中定義的三個接口分別用於處理常規查詢的結果集,遊標查詢的結果集以及存儲過程調用的出參設置。和參數處理器一樣,結果集處理器也只有一個默認實現DefaultResultSetHandler。結果集處理器的功能包括對象的實例化、屬性自動匹配計算、常規屬性賦值、嵌套ResultMap的處理、嵌套查詢的處理、鑑別器結果集的處理等,每個功能我們在分析SQL語句執行selectXXX的時候都詳細的講解過了,具體可以參見selectXXX部分。
測試用例:
package hong.helloWorld.orm.dao.upms;
public interface UserMapper {
List<User> selectByName(@Param("name") String name);
}
<mapper namespace="hong.helloWorld.orm.dao.upms.UserMapper">
<resultMap id="BaseResultMap" type="hong.helloWorld.orm.entity.upms.User">
<id column="ID" jdbcType="VARCHAR" property="id" />
<result column="NAME" jdbcType="VARCHAR" property="name" />
<result column="AGE" jdbcType="DECIMAL" property="age" />
<result column="BIRTHDAY" jdbcType="VARCHAR" property="birthday" />
<result column="CREATE_TIME" jdbcType="DECIMAL" property="createTime" />
</resultMap>
<select id="selectByName" resultMap="BaseResultMap">
select * from TEST_USER where name=#{name}
</select>
</mapper>
@Test
public void Test() throws Exception {
String resource = "hong/helloWorld/Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sqlSessionFactory.openSession();
Map parameter=new HashMap();
parameter.put("name","xiaoming");
List<User> userList = session.selectList("hong.helloWorld.orm.dao.upms.UserMapper.selectByName",parameter);
session.close();
}
現在就來研究一下代碼的執行過程:
在 Test 方法中,第一步,SqlSessionFactoryBuilder讀取 mybaits 的配置文件,創建 SqlSessionFactory;第二步,SqlSessionFactory 創建 SqlSession;第三步,SqlSession 執行數據操作;第四步,關閉。
第一步:SqlSessionFactoryBuilder讀取 mybaits 的配置文件,創建 SqlSessionFactory。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
}
}
public SqlSessionFactory build(Configuration config) {
//mybatis配置文件信息都被存放在Configuration對象中。這裏創建的是DefaultSqlSessionFactory
return new DefaultSqlSessionFactory(config);
}
}
第二步,SqlSessionFactory 創建 SqlSession。
public class DefaultSqlSessionFactory implements SqlSessionFactory {
@Override
public SqlSession openSession() {
// 使用默認的執行器類型(默認是SIMPLE),默認隔離級別,非自動提交 委託給openSessionFromDataSource方法
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
/**
* 通過數據源創建SqlSession
* @param execType mybatis提供了三種執行器類型:SIMPLE, REUSE, BATCH。
* @param level 事務隔離級別
* @param autoCommit 是否自動提交
* @return
*/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
// 獲取事務管理器, 支持從數據源或者直接獲取
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 從數據源創建一個事務,
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
}
}
}
第三步,SqlSession 執行數據操作。
public class DefaultSqlSession implements SqlSession{
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}
}
}
DefaultSqlSession 會將查詢操作交給 Executor對象去完成。交給 Executor前會獲取 MappedStatement 對象,MappedStatement封裝了 sql 的所有信息。
MappedStatement:
由於這裏沒有開啓二級緩存,使用的是默認的Executor實現類SimpleExecutor。
public abstract class BaseExecutor implements Executor {
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 首先根據傳遞的參數獲取BoundSql對象,對於不同類型的SqlSource,對應的getBoundSql實現不同.
BoundSql boundSql = ms.getBoundSql(parameter);
// 創建緩存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 委託給重載的query
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//如果需要刷新緩存(默認DML需要刷新,也可以語句層面修改), 且queryStack(應該是用於嵌套查詢的場景)=0
if (queryStack == 0 && ms.isFlushCacheRequired()) {
// 如果<select>節點配置了flushCache爲true的話,清空一級緩存
clearLocalCache();
}
List<E> list;
try {
queryStack++;
// 如果查詢不需要應用結果處理器,則先從緩存獲取,這樣可以避免數據庫查詢。
// 我們後面會分析到localCache是什麼時候被設置進去的.
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--;
}
if (queryStack == 0) {
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
// 如果設置了一級緩存是STATEMENT級別而非默認的SESSION級別,一級緩存就去掉了
clearLocalCache();
}
}
return list;
}
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 {
// doQuery是個抽象方法,每個具體的執行器都要自己去實現,我們先看SIMPLE的
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 把真正的查詢結果放到緩存中去
localCache.putObject(key, list);
// 如果是存儲過程類型,則把查詢參數放到本地出參緩存中, 所以第一次一定爲空
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
}
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();
// 根據上下文參數和具體的執行器new一個StatementHandler, 其中包含了所有必要的信息,比如結果處理器、參數處理器、執行器等等,
// 主要有三種類型的語句處理器UNPREPARE、PREPARE、CALLABLE。默認是PREPARE類型,通過mapper語句上的statementType屬性進行設置,
// 一般除了存儲過程外不應該設置
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 這一步是真正和JDBC打交道,獲取statement,也包含了對ParameterHandler 的調用。
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
//RoutingStatementHandler根據 statementType 創建不同的 StatementHandler
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());
}
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
}
}
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();//執行 sql
//ResultSetHandler對結果集進行處理
return resultSetHandler.<E> handleResultSets(ps);
}
}