1. 程式人生 > >Mybatis原始碼解析之寫流程

Mybatis原始碼解析之寫流程

閱讀須知

  • Mybatis原始碼版本:3.4.4
  • 註釋規則:
    • //單行註釋做普通註釋
    • /**/多行註釋做深入分析
  • 建議配合Mybatis原始碼閱讀

正文

承接上文,我們繼續來分析寫操作:
DefaultSqlSession:

public int insert(String statement, Object parameter) {
    return update(statement, parameter);
}

DefaultSqlSession:

public int update(String statement, Object parameter) {
    try
{ dirty = true; //獲取MappedStatement MappedStatement ms = configuration.getMappedStatement(statement); /*包裝集合型別的引數(分析查詢流程時已經分析過),執行更新操作*/ return executor.update(ms, wrapCollection(parameter)); } catch (Exception e) { throw ExceptionFactory.wrapException("Error updating database. Cause: "
+ e, e); } finally { ErrorContext.instance().reset(); } }

DefaultSqlSession:

public int delete(String statement, Object parameter) {
    return update(statement, parameter);
}

我們發現insert、update、delete三個方法最終都呼叫了同一個update方法。
CachingExecutor:

public int update(MappedStatement ms, Object parameterObject) throws
SQLException { flushCacheIfRequired(ms); //如果需要,重新整理快取 /*更新操作*/ return delegate.update(ms, parameterObject); }

Mybatis快取我們會用單獨的文章來分析。
BaseExecutor:

public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
        throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache(); //清除本地快取
    return doUpdate(ms, parameter); /*執行更新操作*/
}

SimpleExecutor:

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
        Configuration configuration = ms.getConfiguration();
        //建立StatementHandler(分析查詢流程時已經分析過)
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
        //準備Statement(分析查詢流程時已經分析過)
        stmt = prepareStatement(handler, ms.getStatementLog());
        return handler.update(stmt); /*更新操作*/
    } finally {
        closeStatement(stmt); //關閉Statement
    }
}

PreparedStatementHandler:

public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute(); //執行sql命令
    int rows = ps.getUpdateCount(); //返回影響的行數
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    /*KeyGenerator後置處理(前置處理在建立StatementHandler時應用)*/
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
}

我們以SelectKeyGenerator來分析KeyGenerator的後置處理:
SelectKeyGenerator:

public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (!executeBefore) { //如果不是前置處理
        /*處理生成的key*/
        processGeneratedKeys(executor, ms, parameter);
    }
}

分析之前我們可以先回想一下<selectkey/>標籤的應用,有助於理解下面的處理過程。
SelectKeyGenerator:

private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
        if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
            String[] keyProperties = keyStatement.getKeyProperties(); //獲取配置的目標屬性
            final Configuration configuration = ms.getConfiguration();
            final MetaObject metaParam = configuration.newMetaObject(parameter);
            if (keyProperties != null) {
                //新建執行器
                Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
                //執行查詢
                List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
                //返回結果集長度的校驗
                if (values.size() == 0) {
                    throw new ExecutorException("SelectKey returned no data.");            
                } else if (values.size() > 1) {
                    throw new ExecutorException("SelectKey returned more than one value.");
                } else {
                    MetaObject metaResult = configuration.newMetaObject(values.get(0));
                    //如果只設置了單個目標屬性直接賦值
                    if (keyProperties.length == 1) {
                        if (metaResult.hasGetter(keyProperties[0])) {
                            setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
                        } else {
                            setValue(metaParam, keyProperties[0], values.get(0));
                        }
                    } else {
                        /*設定多個目標屬性的處理*/
                        handleMultipleProperties(keyProperties, metaParam, metaResult);
                    }
                }
            }
        }
    } catch (ExecutorException e) {
        throw e;
    } catch (Exception e) {
        throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
    }
}

SelectKeyGenerator:

private void handleMultipleProperties(String[] keyProperties,
    MetaObject metaParam, MetaObject metaResult) {
    String[] keyColumns = keyStatement.getKeyColumns();
    if (keyColumns == null || keyColumns.length == 0) {
        //沒有指定具體的列,直接使用屬性名稱賦值
        for (String keyProperty : keyProperties) {
            setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
        }
    } else {
        //指定列的數量與屬性的數量不相等丟擲異常
        if (keyColumns.length != keyProperties.length) {
            throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
        }
        for (int i = 0; i < keyProperties.length; i++) {
            //使用列名賦值
            setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
        }
    }
}

最後是對返回影響行數的處理:

private Object rowCountResult(int rowCount) {
    final Object result;
    //根據方法返回值型別將影響行數轉換成對應的型別
    if (method.returnsVoid()) {
        result = null;
    } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
        result = rowCount;
    } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
        result = (long)rowCount;
    } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
        result = rowCount > 0;
    } else {
        throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
    }
    return result;
}

到這裡,Mybatis寫操作的原始碼分析就完成了。