剛回答了一個問題這樣一個問題,mybatis不能正常返回主鍵增加值 下面通過源碼分析一下selectKey都具體實現;關於Mybatis 基於注解Mapper源碼分析 可以看一下具體解析過程。
如果向數據庫中插入一條數據,同時有希望返回該條記錄的主鍵,該怎么處理了?有兩種情況:
- 數據庫主鍵不是自增列,需要預先生成
- 是自增列,插入后才能知道
這兩種情況都可以通過SelectKey解決,第一個種就是before,第二張是after
@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
在簡單介紹了如何使用后,首先看一下KeyGenerator接口中都有那些方法
KeyGenerator接口
/**
* key生成器接口
*/
public interface KeyGenerator {
//在執行主SQL前執行selectKey
void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
//在主SQL執行后執行selectkey
void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}
通過接口都源碼可以發現在KeyGenerator接口中定義了 processBefore 和processAfter 兩個方法,顧名思義就是在在執行SQL前執行和執行SQL后執行,那么下面通過時序圖將其整體調用順序進行一個概覽

如何解析@SelectKey

before具體執行時機
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
// //執行before
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
protected void generateKeys(Object parameter) {
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
ErrorContext.instance().store();
keyGenerator.processBefore(executor, mappedStatement, null, parameter);
ErrorContext.instance().recall();
}
@Override
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
//是否執行前執行,如果不是就不執行
if (executeBefore) {
processGeneratedKeys(executor, ms, parameter);
}
}
after執行時機

@Override
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
//如果executeBefore配置為false則執行
if (!executeBefore) {
processGeneratedKeys(executor, ms, parameter);
}
}
具體執行源碼
private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
try {
//如果parameter不為null同時keyStatement不為null且keyStatement 指定了keyProperties
if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
//獲取keyProperties
String[] keyProperties = keyStatement.getKeyProperties();
//獲取配置信息
final Configuration configuration = ms.getConfiguration();
//獲取參數對象元數據
final MetaObject metaParam = configuration.newMetaObject(parameter);
//其實已經判斷過了
if (keyProperties != null) {
//新建keyExecutor
Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
//執行查詢
List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
//如果查詢結果為0個則拋出異常
if (values.size() == 0) {
throw new ExecutorException("SelectKey returned no data.");
} else if (values.size() > 1) {//查詢的結果個數多余1個則拋出異常
throw new ExecutorException("SelectKey returned more than one value.");
} else {//只返了一個結果值
MetaObject metaResult = configuration.newMetaObject(values.get(0));
//如果keyProperty個數只有1個
if (keyProperties.length == 1) {
//如果查詢結果對象存在這個屬性的getter方法
if (metaResult.hasGetter(keyProperties[0])) {
//將屬性值設置到param中
setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
} else {
//如果沒有getter方法就將當前值設置到屬性中
setValue(metaParam, keyProperties[0], values.get(0));
}
} else {//處理指定多個key屬性場景
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);
}
}
private void handleMultipleProperties(String[] keyProperties,
MetaObject metaParam, MetaObject metaResult) {
//獲取所有key column
String[] keyColumns = keyStatement.getKeyColumns();
//如果key column不存在
if (keyColumns == null || keyColumns.length == 0) {
//沒有指定key column則直接使用配置到 key property
for (String keyProperty : keyProperties) {
setValue(metaParam, keyProperty, metaResult.getValue(keyProperty));
}
} else {
//存在key column 但是數量不一致
if (keyColumns.length != keyProperties.length) {
throw new ExecutorException("If SelectKey has key columns, the number must match the number of key properties.");
}
//數量一致,要求keyColumn 和keyProperty一一對應
for (int i = 0; i < keyProperties.length; i++) {
setValue(metaParam, keyProperties[i], metaResult.getValue(keyColumns[i]));
}
}
}
