<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                ### StatementHandler介紹 StatementHandler?是Mybatis四大組件中另外一個重要的對象,它負責操作Statement對象與數據庫進行交流,在工作時會使用 ParameterHandler對參數進行映射,使用ResultSetHandler對結果進行實體類的綁定。通過接口定義初步了解StatementHandler的功能。 ~~~java public interface StatementHandler { // 使用Connection創建Statement對象,設置事務超時時間 Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException; // 為Statement綁定參數, // 其中會使用ParameterHandler進行參數綁定 void parameterize(Statement statement) throws SQLException; void batch(Statement statement) throws SQLException; // 執行insert/update/delete類型的sql語句 int update(Statement statement) throws SQLException; // 執行select語句 <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; // 獲取BoundSql對象 BoundSql getBoundSql(); // 獲取ParameterHandler對象 ParameterHandler getParameterHandler(); } 復制代碼 ~~~ StatementHandler接口方法不多,圍繞Statement的創建與執行進行設計,通過查看接口定義,可以了解到其主要作用有: * 創建Statement對象、綁定Statement參數; * 執行select、update、insert、delete以及其他類型的sql語句; * 批量執行SQL語句 * 完成結果集向結果對象的轉換; ![image.png](data:image/svg+xml;utf8,) StatementHandler的繼承體系如圖所示,該接口有兩個實現類,其結構與Executor有異曲同工之妙: * BaseStatementHandler:主要提供了構造方法完成對所有字段的初始化工作;實現了prepare接口,以模板化方式創建Statement對象,其中Statement的創建工作由其子類實現。BaseStatementHandler包含三個子類: * SimpleStatementHandler:處理不需要預編譯的SQL語句,類似于JDBC中的Statement; * PreparedStatementHandler:處理需要預編譯的SQL語句,類似于JDBC中的PreparedStatement; * CallableStatementHandler:處理存儲過程,類似于JDBC中的CallableStatement; * RoutingStatementHandler:看了它的實現就會知道,它除了根據statementType創建對應的Handler完成以上三個子類處理器的創建與路由,剩下的就是對接口進行路由,也是“名副其實”。 StatementHandler的生命周期由Executor調度管理,其中使用StatementHandler的流程都在doXxxx方法中,接下來結合源碼對StatementHandler的流程進行分析。 ### 創建流程 由doXxxx方法可知,StatementHandler是通過Configuration#newStatementHandler方法創建的,跟著源碼走下流程: ~~~java //org.apache.ibatis.session.Configuration#newStatementHandler public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 創建StatementHandler時,首先創建的是RoutingStatementHandler這個路由對象; // RoutingStatementHandler會根據ms的類型動態創建相應的StatementHandler StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } //org.apache.ibatis.executor.statement.RoutingStatementHandler#RoutingStatementHandler // 根據statementType創建不同的Handler 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()); } } 復制代碼 ~~~ newStatementHandler方法非常簡單,只有三行:創建RoutingStatementHandler、加載插件、返回已創建的statementHandler對象。 通過“介紹”我們知道RoutingStatementHandler是一個路由類,其內部創建了真正工作的StatementHandler,完成這一工作的就是它的構造方法。從源碼可知,它根據ms.getStatementType()對應的StatementType創建具體的StatementHandler。**那\*\*\*\*ms.getStatementType()是什么類型呢?** 默認情況下,MappedStatement.Builder#Builder把statementType設置為PREPARED。若要修改statementType類型,我們需要在mapper文件的select、insert、update、delete標簽中設置statementType屬性,以select為例: ~~~sql <select id="selectPerson" resultMap="personResultMap" statementType="PREPARED或STATEMENT或CALLABLE"> 復制代碼 ~~~ OK,創建流程分析完成,我們得到了一個RoutingStatementHandler對象,它內部含有對具體StatementHandler的引用。 ### prepare流程 在sql命令執行之前,需要完成Statement的創建及初始化工作,完成這一工作的方式就是方法StatementHandler#prepare。以SimpleExecutor#doQuery為例,來看下prepare流程: ~~~sql @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(); //創建StatementHandler StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); //準備Statement stmt = prepareStatement(handler, ms.getStatementLog()); //執行查詢 return handler.query(stmt, resultHandler); } finally { //關閉Statement closeStatement(stmt); } } // org.apache.ibatis.executor.SimpleExecutor#prepareStatement private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); // 創建Statement對象,這里使用BaseStatementHandler的模板方法按照流程創建并設置參數; // 創建Statement對象的具體邏輯由BaseStatementHandler的子類實現 stmt = handler.prepare(connection, transaction.getTimeout()); // 設置Statement中的參數,這里使用了ParameterHandler handler.parameterize(stmt); return stmt; } 復制代碼 ~~~ 從doQuery到prepareStatement,里面調用了StatementHandler#prepare方法,具體實現類取決于當前handler的類型。默認情況下,會通過RoutingStatementHandler調用到BaseStatementHandler#prepare,它是一個模板方法,通過代碼可知它完成三件工作:實例化Statement、設置超時參數、設置返回數據量范圍。 ~~~java //org.apache.ibatis.executor.statement.BaseStatementHandler#prepare public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { //實例化:抽象方法由子類實現 statement = instantiateStatement(connection); setStatementTimeout(statement, transactionTimeout); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } } 復制代碼 ~~~ 其中,實例化方法instantiateStatement是抽象方法,由BaseStatementHandler的子類實現,大體實現方式類似,我們逐個看下。 #### SimpleStatementHandler#instantiateStatement ~~~java // org.apache.ibatis.executor.statement.SimpleStatementHandler#instantiateStatement @Override protected Statement instantiateStatement(Connection connection) throws SQLException { // resultSetType默認值,直接使用connection創建Statement if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { return connection.createStatement(); } else { // 否則:按照結果集類型及并發模式(只讀)創建 return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } } 復制代碼 ~~~ SimpleStatementHandler的實現只關心ResultSetType,如果是默認(UNSET)則使用不帶參數的createStatement;如果則使用只讀方式創建Statement。 ResultSetType設置ResultSet對象的類型標示可滾動,或者是不可滾動。FORWARD\_ONLY:結果集僅能向前滾動;SCROLL\_INSENSITIVE:結果集前后都可以滾動,但是對數據修改無感知;SCROLL\_SENSITIVE:結果集前后都可以滾動,感知對數據修改。 如果PreparedStatement對象初始化時resultSetType參數設置為FORWARD\_ONLY,在從ResultSet(結果集)中讀取記錄的時,對于訪問過的記錄就自動釋放了內存。而設置為SCROLL\_INSENSITIVE或SCROLL\_SENSITIVE時為了保證能游標能向上移動到任意位置,已經訪問過的所有都保留在內存中不能釋放。所以大量數據加載的時候,就OOM了。 #### PreparedStatementHandler#instantiateStatement ~~~java // org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement // PreparedStatementHandler的實現比SimpleStatementHandler的實現多了對keyGenerator的判斷 @Override protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); // 如果是自增主鍵并且無keyColumns則設置返回自增值。 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() == ResultSetType.DEFAULT) { return connection.prepareStatement(sql); } else { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } } 復制代碼 ~~~ PreparedStatementHandler的實現與SimpleStatementHandler的區別就是增加了數據庫主鍵生成器的判斷邏輯,僅適用于insert和update語句,這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數據庫內部生成的主鍵。 #### CallableStatementHandler#instantiateStatement ~~~java // org.apache.ibatis.executor.statement.CallableStatementHandler#instantiateStatement // 存儲過程的處理邏輯。 @Override protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) { return connection.prepareCall(sql); } else { return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } } 復制代碼 ~~~ CallableStatementHandler處理存儲過程相關的準備工作。 ### parameterize流程 parameterize方法用于sql命令的參數設置,它需要使用ParameterHandler完成對SQL語句中參數占位符的賦值工作。ParameterHandler的工作原理單獨說明,這里不再展開。看下三個實現差別: ~~~java @Override public void parameterize(Statement statement) { // N/A } //PreparedStatementHandler#parameterize @Override public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } //CallableStatementHandler#parameterize @Override public void parameterize(Statement statement) throws SQLException { // 設置輸出參數 registerOutputParameters((CallableStatement) statement); // 與PreparedStatementHandler方式一致 parameterHandler.setParameters((CallableStatement) statement); } 復制代碼 ~~~ ### update流程 update()方法用于執行insert/update/delete命令,日常開發使用頻率較高,RoutingStatementHandler僅做代理轉發,BaseStatementHandler本身沒有實現這個方法,而是由其三個子類直接實現。下面分別看下其實現過程。 #### SimpleStatementHandler#update ~~~java @Override public int update(Statement statement) throws SQLException { String sql = boundSql.getSql(); Object parameterObject = boundSql.getParameterObject(); //獲取mappedStatement的主鍵生成方式 KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); int rows; // 主鍵自增方式 if (keyGenerator instanceof Jdbc3KeyGenerator) { statement.execute(sql, Statement.RETURN_GENERATED_KEYS); rows = statement.getUpdateCount(); //執行完成后,處理返回主鍵值 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else if (keyGenerator instanceof SelectKeyGenerator) { // Sequence Key statement.execute(sql); rows = statement.getUpdateCount(); //執行完成后,處理返回主鍵值 keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject); } else { // 無主鍵返回要求 statement.execute(sql); rows = statement.getUpdateCount(); } return rows; } 復制代碼 ~~~ SimpleStatementHandler#update根據mappedStatement的KeyGenerator類型分為三種情況進行處理,重點在于主鍵值的生成方式,通過processAfter方法設置數據庫返回的主鍵值。 #### PreparedStatementHandler#update ~~~java @Override public int update(Statement statement) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); int rows = ps.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject); return rows; } 復制代碼 ~~~ PrepareStatementHandler處理的是PreparedStatement,調用execute方法后,處理返回主鍵值。 #### CallableStatementHandler#update ~~~java @Override public int update(Statement statement) throws SQLException { CallableStatement cs = (CallableStatement) statement; cs.execute(); int rows = cs.getUpdateCount(); Object parameterObject = boundSql.getParameterObject(); KeyGenerator keyGenerator = mappedStatement.getKeyGenerator(); keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject); resultSetHandler.handleOutputParameters(cs); return rows; } 復制代碼 ~~~ CallableStatementHandler的多了出參處理邏輯。 ### query流程 query方法用于處理select命令,同樣由BaseStatementHandler的三個子類實現,主要有兩個步驟:執行sql、處理返回結果,其中處理返回結果需要使用ResultSetHandler。過程比較簡單,代碼就不再貼出來了。 ### 總結 在Mybatis中,StatementHandler起到了承上啟下的作用,向上與mybatis框架整合在一起,在Executor的協調組織下處理請求,向下有效的組合ParameterHandler、ResultSetHandler等組件,通過不同Statement與JDBC進行“聯絡溝通”。 StatementHandler提供了對Statement生命周期控制及執行的方法,以接口方式暴露給上層的Executor使用。到了StatementHandler這一層,Mybatis也算完成了與JDBC的責任交接,接下來就是JDBC內部的邏輯了。 作者:碼路印記 鏈接:https://juejin.cn/post/6885307966506041357 來源:掘金 著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看