Mybatis源碼-keygen包(Key)

今天主要學習是executor包下一個子包keygen,這個包功能就是自動生成一個key,例如@SelectKey註解設置字段,然後返回,生成key有兩種,一種是通過jdbc生成,一種通過執行sql語句獲取某個字段值實現生成key

1、Jdbc3KeyGenerator

/**
 * 這個類的功能就是在執行完sql獲取 stmt.getGeneratedKeys()生成keys的值,然後通過下標與配置keyProperty("id,name...")進行關聯,通過
 * typeHandler處理之後將值設置到對應keyProperties中,這裏keyProperties是parameter中的字段,所以執行完之後,你可以執行從入參parameter
 * 獲取生成key的值,insert方法返回主鍵也就是利用這個方法。
 * 1.使用jdbc生成key,它只處理執行sql之後
 * 2.生成key,要與入參或出參匹配,然後設置值
 * 3、從結果集中拿出對應屬性的Typehandler,用Typehandler轉換結果集的數據,將值設置到入參中,
 * 4、這裏keyProperties是MappedStatement.keyProperty("id,name")多個以逗號隔開
 *
 *
 * @author Clinton Begin
 * @author Kazuki Shimizu
 */
public class Jdbc3KeyGenerator implements KeyGenerator {

  /**
   * A shared instance.
   * 惡漢模式直接創建一個實例
   * @since 3.4.3
   */
  public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator();

  /**
   * 生成多個key報錯提示信息
   */
  private static final String MSG_TOO_MANY_KEYS = "Too many keys are generated. There are only %d target objects. "
      + "You either specified a wrong 'keyProperty' or encountered a driver bug like #1523.";

  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    // do nothing
  }

  /**
   * 處理完,從數據庫中拿到key
   * @param executor 執行器
   * @param ms  mapper解析成對象
   * @param stmt SQL語句
   * @param parameter 具體參數值
   */
  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    //因爲是執行後獲取生成key,所以不需要executor
    processBatch(ms, stmt, parameter);
  }

  /**
   * 批量處理key
   * @param ms
   * @param stmt
   * @param parameter
   */
  public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
    //如果key屬性對象數組爲空,直接返回
    final String[] keyProperties = ms.getKeyProperties();
    if (keyProperties == null || keyProperties.length == 0) {
      return;
    }
    //獲取生成key結果集
    // 獲取其中一條數據
    //獲取Configuration對象
    try (ResultSet rs = stmt.getGeneratedKeys()) {
      final ResultSetMetaData rsmd = rs.getMetaData();
      final Configuration configuration = ms.getConfiguration();
      // 本身列數小於屬性長度
      if (rsmd.getColumnCount() < keyProperties.length) {
        // Error?
      } else {
        //本身列數>=屬性長度
        assignKeys(configuration, rs, rsmd, keyProperties, parameter);
      }
    } catch (Exception e) {
      throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
    }
  }

  /**
   *  設置key的值
   * @param configuration 配置對象
   * @param rs 結果集(生成key)
   * @param rsmd 結果集源數據
   * @param keyProperties 屬性對象
   * @param parameter 參數
   * @throws SQLException
   */
  @SuppressWarnings("unchecked")
  private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,
      Object parameter) throws SQLException {
    // 判斷類型是不是ParamMap或StrictMap(本質是不是map)
    if (parameter instanceof ParamMap || parameter instanceof StrictMap) {
      // Multi-param or single param with @Param
      assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);
    } else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()
        && ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {
      // Multi-param or single param with @Param in batch operation
      // 多個參數或單個參數用@Param進行批量操作
      assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, ((ArrayList<ParamMap<?>>) parameter));
    } else {
      // Single param without @Param
      //單個參數沒有使用 @Param
      assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);
    }
  }

  /**
   * parameter的字段包含keyProperties, 根據keyProperty獲取parameter對象中setter方法
   * keyProperties是如何與 stmt.getGeneratedKeys() 關聯起來,主要是通過下標進行約定,所以keyProperties下標+1, 變成ResultSet對應值的下標
   * @param configuration 配置對象
   * @param rs 結果集
   * @param rsmd 結果集元數據
   * @param keyProperties key 屬性對象
   * @param parameter 入參具體參數
   * @throws SQLException
   */
  private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
      String[] keyProperties, Object parameter) throws SQLException {
    Collection<?> params = collectionize(parameter);
    if (params.isEmpty()) {
      return;
    }
    List<KeyAssigner> assignerList = new ArrayList<>();
    for (int i = 0; i < keyProperties.length; i++) {
      assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));
    }
    Iterator<?> iterator = params.iterator();
    while (rs.next()) {
      if (!iterator.hasNext()) {
        throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));
      }
      Object param = iterator.next();
      assignerList.forEach(x -> x.assign(rs, param));
    }
  }

  /**
   *  List<Map> list的map參數
   * @param configuration 配置對象
   * @param rs 結果集
   * @param rsmd 結果集元數據
   * @param keyProperties key屬性
   * @param paramMapList 參數map的list
   * @throws SQLException
   */
  private void assignKeysToParamMapList(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
      String[] keyProperties, ArrayList<ParamMap<?>> paramMapList) throws SQLException {
    //獲取參數MapList迭代器
    //創建一個KeyAssigner類
    Iterator<ParamMap<?>> iterator = paramMapList.iterator();
    List<KeyAssigner> assignerList = new ArrayList<>();
    long counter = 0;
    while (rs.next()) {
      if (!iterator.hasNext()) {
        throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));
      }
      ParamMap<?> paramMap = iterator.next();
      if (assignerList.isEmpty()) {
        for (int i = 0; i < keyProperties.length; i++) {
          assignerList
              .add(getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i], keyProperties, false)
                  .getValue());
        }
      }
      assignerList.forEach(x -> x.assign(rs, paramMap));
      counter++;
    }
  }

  /**
   * 將key賦值給參數map
   * @param configuration 配置對象
   * @param rs 結果集
   * @param rsmd 結果集元數據
   * @param keyProperties  key屬性
   * @param paramMap 參數map
   * @throws SQLException
   */
  private void assignKeysToParamMap(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
      String[] keyProperties, Map<String, ?> paramMap) throws SQLException {
    //參數爲空直接返回
    if (paramMap.isEmpty()) {
      return;
    }
    //key= value
    //遍歷keyProperties屬性
    // 參數封裝成KeyAssigner集合
    Map<String, Entry<Iterator<?>, List<KeyAssigner>>> assignerMap = new HashMap<>();
    for (int i = 0; i < keyProperties.length; i++) {
      Entry<String, KeyAssigner> entry = getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i],
          keyProperties, true);
      Entry<Iterator<?>, List<KeyAssigner>> iteratorPair = assignerMap.computeIfAbsent(entry.getKey(),
          k -> entry(collectionize(paramMap.get(k)).iterator(), new ArrayList<>()));
      iteratorPair.getValue().add(entry.getValue());
    }
    long counter = 0;
    while (rs.next()) {
      for (Entry<Iterator<?>, List<KeyAssigner>> pair : assignerMap.values()) {
        if (!pair.getKey().hasNext()) {
          throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));
        }
        Object param = pair.getKey().next();
        pair.getValue().forEach(x -> x.assign(rs, param));
      }
      counter++;
    }
  }

  /**
   *  獲取賦值ParamMap
   * @param config 配置對象
   * @param rsmd  結果集元數據
   * @param columnPosition 列位置
   * @param paramMap 參數map
   * @param keyProperty key 屬性值
   * @param keyProperties key屬性
   * @param omitParamName 是否忽略參數名稱
   * @return
   */
  private Entry<String, KeyAssigner> getAssignerForParamMap(Configuration config, ResultSetMetaData rsmd,
      int columnPosition, Map<String, ?> paramMap, String keyProperty, String[] keyProperties, boolean omitParamName) {
    //單個參數
    boolean singleParam = paramMap.values().stream().distinct().count() == 1;
    //獲取key屬性的逗號的位置
    //如果沒有逗號,判斷一下是不是單個參數
    int firstDot = keyProperty.indexOf('.');
    if (firstDot == -1) {
      if (singleParam) {
        return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);
      }
      throw new ExecutorException("Could not determine which parameter to assign generated keys to. "
          + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
          + "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
          + paramMap.keySet());
    }
    // 設置paramName,從keyProperty中拿
    String paramName = keyProperty.substring(0, firstDot);
    if (paramMap.containsKey(paramName)) {
      String argParamName = omitParamName ? null : paramName;
      String argKeyProperty = keyProperty.substring(firstDot + 1);
      return entry(paramName, new KeyAssigner(config, rsmd, columnPosition, argParamName, argKeyProperty));
    } else if (singleParam) {
      return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);
    } else {
      throw new ExecutorException("Could not find parameter '" + paramName + "'. "
          + "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
          + "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
          + paramMap.keySet());
    }
  }

  /**
   * 獲取只有一個參數屬性
   * @param config 配置對象
   * @param rsmd 結果集元數據
   * @param columnPosition 列位置
   * @param paramMap  參數map
   * @param keyProperty key的屬性
   * @param omitParamName 是否忽略參數名稱
   * @return
   */
  private Entry<String, KeyAssigner> getAssignerForSingleParam(Configuration config, ResultSetMetaData rsmd,
      int columnPosition, Map<String, ?> paramMap, String keyProperty, boolean omitParamName) {
    // Assume 'keyProperty' to be a property of the single param.
    //獲取paramMap的key
    //是否忽略參數名稱,如果忽略就是null,否則取paramMap中的key
    String singleParamName = nameOfSingleParam(paramMap);
    String argParamName = omitParamName ? null : singleParamName;
    return entry(singleParamName, new KeyAssigner(config, rsmd, columnPosition, argParamName, keyProperty));
  }

  /**
   * 從paramMap中拿出第一個key返回
   * @param paramMap
   * @return
   */
  private static String nameOfSingleParam(Map<String, ?> paramMap) {
    // There is virtually one parameter, so any key works.
    return paramMap.keySet().iterator().next();
  }

  /**
   * 具體化參數類型
   * @param param
   * @return
   */
  private static Collection<?> collectionize(Object param) {
    //集合
    // Object[]
    if (param instanceof Collection) {
      return (Collection<?>) param;
    } else if (param instanceof Object[]) {
      return Arrays.asList((Object[]) param);
    } else {
      return Arrays.asList(param);
    }
  }

  private static <K, V> Entry<K, V> entry(K key, V value) {
    // Replace this with Map.entry(key, value) in Java 9.
    // 創建簡單不變的對象
    return new AbstractMap.SimpleImmutableEntry<>(key, value);
  }

  /**
   * 創建一個Key賦值對象
   */
  private class KeyAssigner {
    /**
     * 配置對象
     */
    private final Configuration configuration;

    /**
     * 結果集元數據
     */
    private final ResultSetMetaData rsmd;

    /**
     * 類型處理註冊(聲明一下,什麼類型需要什麼類進行處理)
     */
    private final TypeHandlerRegistry typeHandlerRegistry;

    /**
     * 列位置
     */
    private final int columnPosition;

    /**
     * 參數名稱
     */
    private final String paramName;

    /**
     * 屬性名稱
     */
    private final String propertyName;

    /**
     * 類型處理,把不同類結果用不同處理類進行處理,例如(Integer, 可以自己定義處理這種類型結果)
     */
    private TypeHandler<?> typeHandler;

    protected KeyAssigner(Configuration configuration, ResultSetMetaData rsmd, int columnPosition, String paramName,
        String propertyName) {
      super();
      this.configuration = configuration;
      this.rsmd = rsmd;
      this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
      this.columnPosition = columnPosition;
      this.paramName = paramName;
      this.propertyName = propertyName;
    }

    /**
     * 賦值操作
     * @param rs 結果集
     * @param param 參數
     */
    protected void assign(ResultSet rs, Object param) {
      //如果參數名稱不爲空,替換param
      if (paramName != null) {
        // If paramName is set, param is ParamMap
        param = ((ParamMap<?>) param).get(paramName);
      }
      //將param分解元對象
      MetaObject metaParam = configuration.newMetaObject(param);
      try {
        //如果類型處理爲null
        //如果有這個屬性名的setter的方法
        //主要是獲取typeHandler對象
        if (typeHandler == null) {
          if (metaParam.hasSetter(propertyName)) {
            Class<?> propertyType = metaParam.getSetterType(propertyName);
            typeHandler = typeHandlerRegistry.getTypeHandler(propertyType,
                JdbcType.forCode(rsmd.getColumnType(columnPosition)));
          } else {
            throw new ExecutorException("No setter found for the keyProperty '" + propertyName + "' in '"
                + metaParam.getOriginalObject().getClass().getName() + "'.");
          }
        }
        if (typeHandler == null) {
          // Error?
        } else {
          //獲取結果的值
          Object value = typeHandler.getResult(rs, columnPosition);
          metaParam.setValue(propertyName, value);
        }
      } catch (SQLException e) {
        throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e,
            e);
      }
    }
  }
}

1.1、總結

  1. 這個是執行後獲取生成key,也就是sql執行完之後
  2. 設置入參某一些字段作爲Key, 然後執行完sql之後,將生成值setter到入參對應的字段中,對於不同類型進行不同處理

2、SelectKeyGenerator

/**
 * 通過select的sql語句獲取key
 * @author Clinton Begin
 * @author Jeff Butler
 */
public class SelectKeyGenerator implements KeyGenerator {

  /**
   * selectkey前綴
   */
  public static final String SELECT_KEY_SUFFIX = "!selectKey";
  /**
   * 是否在執行sql之前
   */
  private final boolean executeBefore;

  /**
   * 獲取key的sql語句
   */
  private final MappedStatement keyStatement;

  public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
    this.executeBefore = executeBefore;
    this.keyStatement = keyStatement;
  }

  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    if (!executeBefore) {
      processGeneratedKeys(executor, ms, parameter);
    }
  }

  /**
   * 通過執行sql獲取keys
   * @param executor 執行器
   * @param ms  mapper語句
   * @param parameter 參數對象
   */
  private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      //入參不爲空,查詢key的sql不能爲空,當然字段key也不能爲空
      //獲取key屬性
      //獲取configuration配置對象
      //將參數對象解析成元對象
      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) {
          // Do not close keyExecutor.
          // The transaction will be closed by parent executor.
          // 創建一個Executor對象
          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])) {
                //設置返回values.get(0)對象的某個字段的值
                setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
              } else {
                // no getter for the property - maybe just a single value object
                // so try that
                //只有本身就是有一個值
                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);
    }
  }

  /**
   * 處理多個properties key
   * @param keyProperties keyProperties
   * @param metaParam 返回參數元對象
   * @param metaResult 從數據庫查詢結果元對象
   */
  private void handleMultipleProperties(String[] keyProperties,
      MetaObject metaParam, MetaObject metaResult) {
    String[] keyColumns = keyStatement.getKeyColumns();
    //如果沒有設置keyColumns不需要校驗 keyColumns的長度必須等於keyProperties的長度
    if (keyColumns == null || keyColumns.length == 0) {
      // no key columns specified, just use the property names
      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]));
      }
    }
  }

  /**
   * 設置值
   * @param metaParam 返回給parameter
   * @param property 字段名
   * @param value 值
   */
  private void setValue(MetaObject metaParam, String property, Object value) {
    if (metaParam.hasSetter(property)) {
      metaParam.setValue(property, value);
    } else {
      throw new ExecutorException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
    }
  }
}

2.1、總結

  1. 這個通過配置SQL語句執行一次查詢來獲取key對應的值
  2. 同時這個有兩種情況,可以在執行正式sql之前,也可以之後
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章