MyabtisPlus(3):插入时主键策略

650 阅读2分钟

主键策略

MybatisReuseExecutor->doUpdate():
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); // --1
    Statement stmt = prepareStatement(handler, ms.getStatementLog(), false);
    return stmt == null ? 0 : handler.update(stmt); // --2
Configuration->newStatementHandler(): // --1
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
RoutingStatementHandler->RoutingStatementHandler():
    delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
PreparedStatementHandler->PreparedStatementHandler():
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
BaseStatementHandler->BaseStatementHandler():
    if (boundSql == null) { 
      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);
Configuration->newParameterHandler():
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
MybatisXMLLanguageDriver->createParameterHandler():
    return new MybatisDefaultParameterHandler(mappedStatement, parameterObject, boundSql);
MybatisDefaultParameterHandler->MybatisDefaultParameterHandler():
    super(mappedStatement, processBatch(mappedStatement, parameterObject), boundSql);
MybatisDefaultParameterHandler->processBatch():
    return populateKeys(metaObjectHandler, tableInfo, ms, parameterObject, isInsert);
// 根据反射信息查看实体类的主键字段与主键填充策略,当type设定为填充的字段时,会填充
// 源码注释已经很详细了
MybatisDefaultParameterHandler->populateKeys():

自增主键映射返回

RoutingStatementHandler->update():
    return delegate.update(statement);
PreparedStatementHandler->update():
    // ps内部封装了PreparedStatement,最终执行的是ps的execute()
    // 插入成功时会返回并赋值ps的stmt的statment的delegate的results的updateId和updateCount,会把updateId赋给ps的stmt的statment的delegate的lastInsertId
    ps.execute();
    // 返回更新条数
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    // 获取主键类型,type=AUTO的时候返回的是Jdbc3KeyGenerator类
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    // Jdbc3KeyGenerator会对实体类主键作一些操作
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
Jdbc3KeyGenerator->processAfter():
    processBatch(ms, stmt, parameter);
Jdbc3KeyGenerator->processBatch():
    // 主键名称
    final String[] keyProperties = ms.getKeyProperties();
    // 底层调用ps.getGeneratedKeys(),新建一个ResultSetImpl对象,伪造返回字段名为GENERATED_KEY,值为lastInsertId
    try (ResultSet rs = stmt.getGeneratedKeys()) {
        ......
        // 主键赋值
        assignKeys(configuration, rs, rsmd, keyProperties, parameter);
    }

主键赋值操作最终进入下面这个方法

Jdbc3KeyGenerator->KeyAssigner->assign():
    // 最终调用的是rs.getInt(1),获得了返回的主键id
    Object value = typeHandler.getResult(rs, columnPosition);
    // 调用method.invoke(target, args)给对象赋值,propertyName是主键字段名,这里就相当于tree.setId(value)
    // 这样,自动填充主键就算是完成了
    metaParam.setValue(propertyName, value);

FAQ:

  • 当type为AUTO的时候,为什么即使设置了主键值,插入时仍然不会插入主键id?
    save方法属于动态sql源,当type为AUTO的时候,SqlNode不会有关于id的部分
TableInfo->getKeyInsertSqlColumn():
    if (StringUtils.isNotEmpty(keyColumn)) {
        if (idType == IdType.AUTO) {
            return EMPTY;
        }
        return keyColumn + COMMA + (newLine ? NEWLINE : EMPTY);
    }
    return EMPTY;