KeyGenerator

public interface KeyGenerator {
  // 在执行数据更新前生成主键
  void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
  // 在执行数据更新后生成主键
  void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);

}

Jdbc3KeyGenerator

<!--useGeneratedKeys="true"代表我们希望在插入数据库之后获取生成的主键值-->
<insert id="insertOne" parameterMap="parameterMap1515" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
    insert into test_extend_and_automapping (`myname`, `myage`)
    values (#{myname}, #{myage})
</insert>
/**
 * @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();

  //所以该方法不需要实现
  @Override
  public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    // do nothing
  }

  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    processBatch(ms, stmt, parameter);
  }

  /**
   *
   * @param ms 代表一个完整的sql
   * @param stmt 代表一个 selectKey对象
   * @param parameter 存储传入参数信息的集合
   *                  有可能是这这样的 如可能:TestExtendAndAutoMapping{id=null, myname='7868c188-241e-491a-b951-9624efc2044c', myage=24}
   */
  public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
    //获取selectkey标签的keyProperty属性 一个selectKey只会有一个,这里只有一个
    final String[] keyProperties = ms.getKeyProperties();
    if (keyProperties == null || keyProperties.length == 0) {
      return;
    }
    //获取JDBC的ResultSet对象,封装有数据库生成的主键信息(因此,mybaits最终还是要依赖于JDBC的机制)
    try (ResultSet rs = stmt.getGeneratedKeys()) {
      //获取全局配置对象
      final Configuration configuration = ms.getConfiguration();
      //获取结果集中元数据信息,并且元数据个数要大于等于keyProperty的数量
      if (rs.getMetaData().getColumnCount() >= keyProperties.length) {
        Object soleParam = getSoleParameter(parameter);
        //获取参数值
        if (soleParam != null) {//只有一个参数情况
          assignKeysToParam(configuration, rs, keyProperties, soleParam);
        } else {//多个参数的情况
          assignKeysToOneOfParams(configuration, rs, keyProperties, (Map<?, ?>) parameter);
        }
      }
    } catch (Exception e) {
      throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
    }
  }

  protected void assignKeysToOneOfParams(final Configuration configuration, ResultSet rs, final String[] keyProperties,
      Map<?, ?> paramMap) throws SQLException {
    // Assuming 'keyProperty' includes the parameter name. e.g. 'param.id'.
    int firstDot = keyProperties[0].indexOf('.');
    if (firstDot == -1) {
      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());
    }
    String paramName = keyProperties[0].substring(0, firstDot);
    Object param;
    if (paramMap.containsKey(paramName)) {
      param = paramMap.get(paramName);
    } 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());
    }
    // Remove param name from 'keyProperty' string. e.g. 'param.id' -> 'id'
    String[] modifiedKeyProperties = new String[keyProperties.length];
    for (int i = 0; i < keyProperties.length; i++) {
      if (keyProperties[i].charAt(firstDot) == '.' && keyProperties[i].startsWith(paramName)) {
        modifiedKeyProperties[i] = keyProperties[i].substring(firstDot + 1);
      } else {
        throw new ExecutorException("Assigning generated keys to multiple parameters is not supported. "
            + "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());
      }
    }
    assignKeysToParam(configuration, rs, modifiedKeyProperties, param);
  }

  private void assignKeysToParam(final Configuration configuration, ResultSet rs, final String[] keyProperties,
      Object param)
      throws SQLException {
    //类型注册器
    final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    //获取结果集元数据对象
    final ResultSetMetaData rsmd = rs.getMetaData();
    // Wrap the parameter in Collection to normalize the logic.
    Collection<?> paramAsCollection = null;//最后都转化为Collection
    if (param instanceof Object[]) {//参数是数组
      paramAsCollection = Arrays.asList((Object[]) param);
    } else if (!(param instanceof Collection)) {//是集合
      paramAsCollection = Arrays.asList(param);
    } else {
      paramAsCollection = (Collection<?>) param;
    }
    TypeHandler<?>[] typeHandlers = null;
    for (Object obj : paramAsCollection) {
      if (!rs.next()) {
        break;
      }
      //将传入参数封装为MetaObject方便操作,比如操作其属性值等 利用反射 给主键赋值
      MetaObject metaParam = configuration.newMetaObject(obj);
      if (typeHandlers == null) {
        //获取类型处理器 以便赋值操作
        typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
      }
      //填充主键值到对象中
      populateKeys(rs, metaParam, keyProperties, typeHandlers);
    }
  }

  private Object getSoleParameter(Object parameter) {
    //ParamMap,StrictMap 是DefaultSqlSession的静态内部类,都继承了HashMap
    //判断参数的类型不是HashMap 则直接返回
    if (!(parameter instanceof ParamMap || parameter instanceof StrictMap)) {
      return parameter;
    }
    //如果是Map,则遍历
    Object soleParam = null;
    for (Object paramValue : ((Map<?, ?>) parameter).values()) {
      if (soleParam == null) {//如果soleParam==null,则将paramValue赋值给如果soleParam
        soleParam = paramValue;
      } else if (soleParam != paramValue) {//如果soleParam != paramValue,则将soleParam置为null,并且返回soleParam
        soleParam = null;
        break;
      }
    }
    return soleParam;
  }

  private TypeHandler<?>[] getTypeHandlers(TypeHandlerRegistry typeHandlerRegistry, MetaObject metaParam, String[] keyProperties, ResultSetMetaData rsmd) throws SQLException {
    TypeHandler<?>[] typeHandlers = new TypeHandler<?>[keyProperties.length];
    for (int i = 0; i < keyProperties.length; i++) {
      if (metaParam.hasSetter(keyProperties[i])) {
        Class<?> keyPropertyType = metaParam.getSetterType(keyProperties[i]);
        typeHandlers[i] = typeHandlerRegistry.getTypeHandler(keyPropertyType, JdbcType.forCode(rsmd.getColumnType(i + 1)));
      } else {
        throw new ExecutorException("No setter found for the keyProperty '" + keyProperties[i] + "' in '"
            + metaParam.getOriginalObject().getClass().getName() + "'.");
      }
    }
    return typeHandlers;
  }

  private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler<?>[] typeHandlers) throws SQLException {
    for (int i = 0; i < keyProperties.length; i++) {//其实keyProperties长度是1
      String property = keyProperties[i];
      TypeHandler<?> th = typeHandlers[i];
      if (th != null) {
        Object value = th.getResult(rs, i + 1);
        //利用反射调用set方法赋值
        metaParam.setValue(property, value);
      }
    }
  }
}

SelectKeyGenerator

/**
 * @author Clinton Begin
 * @author Jeff Butler
 *
 * <insert id="insertByQueryParamter" parameterMap="myParameterMap">
        <selectKey keyProperty="id" keyColumn="id"
        resultType="java.lang.String" order="BEFORE">
        select uuid()
        </selectKey>
        INSERT INTO `test_selectkey`
        (`id`,
        `myname`,
        `myage`)
        VALUES (#{id},
        #{myname},
        #{myage})
    </insert>

    <insert id="insertOneSelectKeyAfter" parameterMap="parameterMap1515">
        <selectKey keyProperty="id"
        keyColumn="id"
        order="AFTER"
        resultType="java.lang.Integer">
        select LAST_INSERT_ID()
        </selectKey>
        insert into test_extend_and_automapping (`myname`, `myage`)
        values (#{myname}, #{myage})
    </insert>
order=after executeBefore = ture
order=before  executeBefore = false
SelectKeyGenerator 可以在操作数据库之前或之后获取主键
executeBefore = false则调用processBefore 在操作数据库之前获取主键 通过select uuid()生成主键值,并设置到对应的属性中
executeBefore = ture  则调用processAfter 在操作数据库之后获取主键 由数据库生成的主键
 */
public class SelectKeyGenerator implements KeyGenerator {

  public static final String SELECT_KEY_SUFFIX = "!selectKey";
  private final boolean executeBefore;
  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);
    }
  }

  private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
    try {
      if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
        // 获取配置的属性值,即<selectKey>标签中配置的keyProperty="id"
        String[] keyProperties = keyStatement.getKeyProperties();//这里的keyProperties.length =1 只有一个属性
        //获取全局配置
        final Configuration configuration = ms.getConfiguration();
        //将属性封装成metaObject 方便赋值操作
        final MetaObject metaParam = configuration.newMetaObject(parameter);
        if (keyProperties != null) {
          // Do not close keyExecutor.
          // The transaction will be closed by parent executor.
          //获取执行器
          Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
          //执行<selectKey>中的sql 获取主键值 这里的主键值也只有一个
          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) {//这里只考虑一个值的情况
              //使用mybatis,实体类中 可以没有getter方法,但是不能没有setter,因为赋值操作是通过反射完成的,setValue,如果没有setter方法,则报错
                if (metaResult.hasGetter(keyProperties[0])) {//判断主键属性是否有getter方法
                // 如果是传入的对象的当前属性有getter方法,一般获取的是
                // 字符串,所有肯定没有
                setValue(metaParam, keyProperties[0], metaResult.getValue(keyProperties[0]));
              } else {
                // 设置属性值
                // metaParam:用户传入的参数对象
                // keyProperties[0]:要设置的属性值,即id
                // values.get(0):生成的主键值
                // 将生成的主键值设置到传入参数对象的属性中
                // 如下是设置前,设置后的值:
                // 设置前:{"myage":19,"myname":"379bff59-eb6d-4b03-95d4-571379e9d13f"}
                // 设置后:{"id":"db6fbe60-07af-11ec-978a-1b6197d25533","myage":19,"myname":"379bff59-eb6d-4b03-95d4-571379e9d13f"}
                // 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);
    }
  }
    //这里应该是处理联合主键
  private void handleMultipleProperties(String[] keyProperties,
      MetaObject metaParam, MetaObject metaResult) {
    String[] keyColumns = keyStatement.getKeyColumns();

    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]));
      }
    }
  }

  private void setValue(MetaObject metaParam, String property, Object value) {
    if (metaParam.hasSetter(property)) {
        //通过反射调用setter方法赋值
      metaParam.setValue(property, value);
    } else {
      throw new ExecutorException("No setter found for the keyProperty '" + property + "' in " + metaParam.getOriginalObject().getClass().getName() + ".");
    }
  }
}

NoKeyGenerator

不设置主键值,也不获取数据库生成的主键值的情况,`为了保持代码的通用性

public class NoKeyGenerator implements KeyGenerator {

  public static final NoKeyGenerator INSTANCE = new NoKeyGenerator();

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

  @Override
  public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
    // Do Nothing
  }

}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐