<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Mybatis執行器

      mybatis執行sql語句的操作是由執行器(Executor)完成的,mybatis中一共提供了3種Executor

      類型 名稱 功能
      REUSE 重用執行器 緩存PreparedStatement,下一次執行相同的sql可重用
      BATCH 批量執行器 將修改操作記錄在本地,等待程序觸發或有下一次查詢時才批量執行修改操作
      SIMPLE 簡單執行器 對每一次執行都生成PreparedStatement,執行完就關閉,不緩存

      另外,mybatis 還提供了一個緩存執行器CachingExecutor,該執行器實際上是以上三種執行器的裝飾類,用以處理緩存相關操作,實際干活的還是以上三種執行器之一。

      Executor的繼續結構如下:

      image

      1. BaseExecutor

      BaseExecutor實現了Executor的基本操作,如:

      • 事務的處理:

        • commit(...):處理事務的提交
        • rollback(...):處理事務的回滾
      • 緩存的處理:

        • createCacheKey(...):創建緩存key
        • clearLocalCache(...):清除緩存
      • curd操作:

        • query(...):查詢操作
        • update(...):更新操作,插入與刪除也是在這里處理
      • 留待子類的實現

        • doUpdate(...):具體的更新操作,留待子類實現
        • doQuery(...):具體的查詢操作,留待子類實現

      接下來我們關注Executor的實現時,只關注留待子類實現的方法。

      2. SimpleExecutor

      SimpleExecutor會對每一次執行都生成PreparedStatement,執行完就關閉,不緩存,我們來看看它是怎么實現的,來看看它的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();
      	  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, 
      			rowBounds, resultHandler, boundSql);
      	  // 得到 PrepareStatement
      	  stmt = prepareStatement(handler, ms.getStatementLog());
      	  // 執行查詢
      	  return handler.query(stmt, resultHandler);
      	} finally {
      	  // 關閉 Statement
      	  closeStatement(stmt);
      	}
        }
      

      獲取Statement的方法為SimpleExecutor#prepareStatement

        private Statement prepareStatement(StatementHandler handler, Log statementLog) 
      		throws SQLException {
      	Statement stmt;
      	// 獲取數據庫連接
      	Connection connection = getConnection(statementLog);
      	// 獲取 Statement
      	stmt = handler.prepare(connection, transaction.getTimeout());
      	// 處理參數設置
      	handler.parameterize(stmt);
      	return stmt;
        }
      

      這個方法先是獲取了數據庫連接,接著獲取Statement,然后處理了參數設置。

      關于數據庫連接的獲取,我們在分析配置文件的解析時,數據源的配置最終會轉化成PooledDataSourceUnpooledDataSource對象,數據庫連接就是從數據源來的。

      至于Statement的生成,PreparedStatement的實例化操作方法為PreparedStatementHandler#instantiateStatement,這些都是常規的jdbc操作,就不細看了。

      處理sql的執行方法為PreparedStatementHandler#query

        @Override
        public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
      	PreparedStatement ps = (PreparedStatement) statement;
      	// 執行
      	ps.execute();
      	return resultSetHandler.handleResultSets(ps);
        }
      

      SimpleExecutor#doQuery(...)的執行流程如下:

      • 獲取數據庫連接
      • 獲取PrepareStatement
      • 執行查詢
      • 關閉PrepareStatement

      SimpleExecutor的操作就是常規的jdbc操作。

      3. ReuseExecutor

      ReuseExecutor會緩存PreparedStatement,下一次執行相同的sql可重用。

      我們依然分析doQuery(...)方法:

        public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, 
      		ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
      	Configuration configuration = ms.getConfiguration();
      	StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, 
      			rowBounds, resultHandler, boundSql);
      	// 獲取 Statement
      	Statement stmt = prepareStatement(handler, ms.getStatementLog());
      	// 處理查詢操作
      	return handler.query(stmt, resultHandler);
        }
      

      SimpleExecutor相比,ReuseExecutordoQuery(...)方法并沒關閉Statement.我們來看看Statement的獲取操作:

      private Statement prepareStatement(StatementHandler handler, Log statementLog) 
      		throws SQLException {
      	Statement stmt;
      	BoundSql boundSql = handler.getBoundSql();
      	String sql = boundSql.getSql();
      	// 根據sql語句判斷是否有Statement緩存
      	if (hasStatementFor(sql)) {
      	  // 有緩存,直接使用
      	  stmt = getStatement(sql);
      	  applyTransactionTimeout(stmt);
      	} else {
      	  // 沒緩存,獲取數據庫連接,再獲取 Statement
      	  Connection connection = getConnection(statementLog);
      	  stmt = handler.prepare(connection, transaction.getTimeout());
      	  // 緩存 Statement
      	  putStatement(sql, stmt);
      	}
      	// 處理參數
      	handler.parameterize(stmt);
      	return stmt;
      }
      

      可以看到,ReuseExecutor獲取Statement時,會先從緩存里獲取,緩存里沒有才會新建一個Statement,然后將新建的Statement添加到緩存中。從這里可以看出,ReuseExecutorReuse,復用的是Statement

      我們再來看看緩存Statement的結構:

      public class ReuseExecutor extends BaseExecutor {
        private final Map<String, Statement> statementMap = new HashMap<>();
        ...
        private Statement getStatement(String s) {
      	return statementMap.get(s);
        }
        private void putStatement(String sql, Statement stmt) {
      	statementMap.put(sql, stmt);
        }
      }
      

      由些可見,緩存Statement的是一個Mapkeysql語句,valueStatement.

      4. BatchExecutor

      BatchExecutor會將修改操作記錄在本地,等待程序觸發或有下一次查詢時才批量執行修改操作,即:

      • 進行修改操作(insertupdatedelete)時,并不會立即執行,而是會緩存到本地
      • 進行查詢操作(select)時,會先處理緩存到本地的修改操作,再進行查詢操作
      • 也可行觸發修改操作

      從以上內容來看,這種方式似乎有大坑,列舉幾點如下:

      • 修改操作緩存到本地后,如果執行前遇到意外重啟,緩存的記錄會不會丟失?
      • 分布式環境下,多機共同協作,更新在A機上執行,查詢在B機上執行,B機是不是不能查到B機的更新記錄(B機的更新操作還在緩存中,并未執行)?

      我們來看下BatchExecutor的更新操作,進入doUpdate(...)方法:

        public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
      	final Configuration configuration = ms.getConfiguration();
      	final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, 
      			RowBounds.DEFAULT, null, null);
      	final BoundSql boundSql = handler.getBoundSql();
      	final String sql = boundSql.getSql();
      	final Statement stmt;
      	// 如果傳入的sql是當前保存的 sql,直接使用
      	if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      	  int last = statementList.size() - 1;
      	  stmt = statementList.get(last);
      	  applyTransactionTimeout(stmt);
      	  handler.parameterize(stmt);// fix Issues 322
      	  BatchResult batchResult = batchResultList.get(last);
      	  batchResult.addParameterObject(parameterObject);
      	} else {
      	  // 創建連接,獲取 Statement
      	  Connection connection = getConnection(ms.getStatementLog());
      	  stmt = handler.prepare(connection, transaction.getTimeout());
      	  handler.parameterize(stmt);    // fix Issues 322
      	  currentSql = sql;
      	  currentStatement = ms;
      	  statementList.add(stmt);
      	  batchResultList.add(new BatchResult(ms, sql, parameterObject));
      	}
      	// 保存,等待之后批量執行
      	handler.batch(stmt);
      	return BATCH_UPDATE_RETURN_VALUE;
        }
      

      BatchExecutor有成員變量會記錄上一次執行的sqlMappedStatement,如果本次執行的sqlMappedStatement與上一次執行的相同,則直接使用上一次的Statement,否則就新建連接、獲取Statement.

      得到Statement后,會調用PreparedStatementHandler#batch方法:

        public void batch(Statement statement) throws SQLException {
      	PreparedStatement ps = (PreparedStatement) statement;
      	ps.addBatch();
        }
      

      這個方法并沒有執行,只是調用PreparedStatement#addBatch方法,將當前statement保存了起來。

      PreparedStatement#addBatch方法如何使用呢?簡單示意下:

      // 獲取連接
      Connection connection = getConnection();
      // 預編譯sql
      String sql = "xxx";
      PreparedStatement statement = connection.prepareStatement(sql);   
      //記錄1
      statement.setInt(1, 1);
      statement.setString(2, "one");
      statement.addBatch();   
      //記錄2
      statement.setInt(1, 2);
      statement.setString(2, "two");
      statement.addBatch();   
      //記錄3
      statement.setInt(1, 3);
      statement.setString(2, "three");
      statement.addBatch();   
      //批量執行
      int[] counts = statement.executeBatch();
      // 關閉statment,關閉連接
      ...
      

      BatchExecutordoUpdate(...)方法并沒有執行sql語句,我們再來看看doQuery(...)方法:

        public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
      		ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
      	Statement stmt = null;
      	try {
      	  // 處理緩存中的 statements
      	  flushStatements();
      	  Configuration configuration = ms.getConfiguration();
      	  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, 
      			rowBounds, resultHandler, boundSql);
      	  // 獲取連接,獲取Statement,處理參數
      	  Connection connection = getConnection(ms.getStatementLog());
      	  stmt = handler.prepare(connection, transaction.getTimeout());
      	  handler.parameterize(stmt);
      	  // 執行查詢
      	  return handler.query(stmt, resultHandler);
      	} finally {
      	  // 關閉 Statement
      	  closeStatement(stmt);
      	}
        }
      

      doQuery(...)方法會先調用flushStatements()方法,然后再處理查詢操作,整個過程基本同SimpleExecutor一致,即”獲取數據庫連接-獲取Statement-處理查詢-關閉Statement“等幾步。我們重點來看flushStatements()方法的流程.

      flushStatements()方法最終調用的是BatchExecutor#doFlushStatements方法,代碼如下:

        public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
      	try {
      	  List<BatchResult> results = new ArrayList<>();
      	  if (isRollback) {
      		return Collections.emptyList();
      	  }
      	  // 遍歷的statementList,statementList就是緩存statement的結構
      	  for (int i = 0, n = statementList.size(); i < n; i++) {
      		Statement stmt = statementList.get(i);
      		applyTransactionTimeout(stmt);
      		BatchResult batchResult = batchResultList.get(i);
      		try {
      		  // 關鍵代碼:stmt.executeBatch(),批量執行sql
      		  batchResult.setUpdateCounts(stmt.executeBatch());
      		  ...
      		} catch (BatchUpdateException e) {
      		  ...
      		}
      		results.add(batchResult);
      	  }
      	  return results;
      	} finally {
      	  ...
      	}
        }
      

      BatchExecutor#doFlushStatements方法的關鍵代碼就是batchResult.setUpdateCounts(stmt.executeBatch());了 ,其中的stmt.executeBatch()就是批量執行更新操作了。

      從以上分析可知,BatchExecutor#doUpdate(...)方法不會執行sql語句,只是把sql語句轉換為Statement然后緩存起來,在執行BatchExecutor#doQuery(...)方法時,會先執行緩存起來的Statement,然后再執行查詢操作,當然也可以手動調用BatchExecutor#flushStatements方法執行緩存的Statement

      5. CachingExecutor

      CachingExecutor不同于以上3種執行器,它是一個裝飾類,可以從緩存中獲取數據,實際干活的還是以上三種執行器之一:

      public class CachingExecutor implements Executor {
        // 具體的執行器
        private final Executor delegate;
        private final TransactionalCacheManager tcm = new TransactionalCacheManager();
        public CachingExecutor(Executor delegate) {
      	this.delegate = delegate;
      	delegate.setExecutorWrapper(this);
        }
        ...
      }
      

      從代碼來看,它是Executor的子類,其中有一個成員變量delegate,它的類型為Executor,由構造方法傳入。也就是說,在創建CachingExecutor時,會傳入以上3種執行器之一,CachingExecutor會把它保存到成員變量delegate中。

      CachingExecutor的query(...)方法如下:

        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
      		ResultHandler resultHandler) throws SQLException {
      	BoundSql boundSql = ms.getBoundSql(parameterObject);
      	// 創建緩存key
      	CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
      	return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
        @Override
        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, 
      		ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      	  throws SQLException {
      	Cache cache = ms.getCache();
      	// 操作緩存
      	if (cache != null) {
      	  flushCacheIfRequired(ms);
      	  if (ms.isUseCache() && resultHandler ** null) {
      		ensureNoOutParams(ms, boundSql);
      		@SuppressWarnings("unchecked")
      		// 從緩存中獲取  
      		List<E> list = (List<E>) tcm.getObject(cache, key);
      		if (list ** null) {
      		  // 實際處理查詢的操作  
      		  list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
      		  // 添加到緩存中
      		  tcm.putObject(cache, key, list); // issue #578 and #116
      		}
      		return list;
      	  }
      	}
      	return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
      

      從代碼上來看,CachingExecutor在處理查詢時,會先從緩存中獲取,當緩存中不存在時,就執行具體執行器的query(xxx)方法。

      posted @ 2024-07-03 19:34  二價亞鐵  閱讀(286)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产对白老熟女正在播放| 亚洲综合av男人的天堂| 国产日产欧产美韩系列麻豆| 高级会所人妻互换94部分| 亚洲区日韩精品中文字幕| 国产精品三级一区二区三区| 国产福利免费在线观看| 欧美日韩精品一区二区三区高清视频| 成人免费无码不卡毛片| 亚洲日韩精品无码一区二区三区 | 国产午夜无码视频在线观看| 人妻 日韩精品 中文字幕| 豆国产97在线 | 亚洲| 色综合久久精品亚洲国产| 亚洲更新最快无码视频| 亚洲国产午夜精品理论片妓女| 亚洲国产一区二区av| 亚洲色一色噜一噜噜噜| 欧洲lv尺码大精品久久久| 亚洲乱码国产乱码精品精| 91中文字幕一区二区| 欧美激情精品久久久久久| 久久国产热这里只有精品| 国产色无码精品视频免费| 国产精品ⅴ无码大片在线看| 四虎永久免费精品视频| 人妻日韩精品中文字幕| 国产亚洲精品自在久久| 国产精品永久久久久久久久久| 国产成人高清亚洲综合| 午夜dv内射一区二区| 国产精品熟女亚洲av麻豆| 国产中文字幕精品免费| 久久国产精品久久精品国产| 成人亚洲a片v一区二区三区动漫| 国产对白老熟女正在播放| 亚洲色偷拍区另类无码专区| 亚洲中文字幕精品第三区| 精品一卡2卡三卡4卡乱码精品视频| 日韩精品一区二区三区在线观看 | 国产一区二区三区内射高清|