mybatis-3.4.6 自增主键

系列

  • mybatis-3.4.6 配置介绍
  • mybatis-3.4.6 顶层配置解析
  • mybatis-3.4.6 子配置解析
  • mybatis-3.4.6 mapper解析
  • mybatis-3.4.6 SQL执行流程
  • mybatis-3.4.6 SqlSession执行过程
  • mybatis-3.4.6 缓存介绍
  • mybatis-3.4.6 自增主键
  • mybatis-3.4.6 foreach 自增主键
  • mybatis-3.4.6 事务管理


开篇

  • 这个系列是基于mybatis-3.4.6版本的源码解析,这篇文章主要分析mybatis的主键生成的逻辑。

  • 这篇文章会阐述JDBC本身获取自增主键的原理和mybatis处理自增主键的逻辑。

  • mybatis针对自增主键的处理是建立在JDBC的Statement的getGeneratedKeys基础上,所以本质还是得理解JDBC针对自增主键的处理逻辑。


JDBC自增主键

Class.forName("com.mysql.jdbc.Driver");

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "123");
conn.setAutoCommit(false);
PreparedStatement pstm = conn.prepareStatement("insert into students(name, email) values(?, ?)",
Statement.RETURN_GENERATED_KEYS);

pstm.setString(1, "name1");
pstm.setString(2, "email1");
pstm.addBatch();
pstm.setString(1, "name2");
pstm.setString(2, "email2");
pstm.addBatch();
pstm.executeBatch();

// 返回自增主键值
ResultSet rs = pstm.getGeneratedKeys();
while (rs.next()) {
        Object value = rs.getObject(1);
        System.out.println(value);
    }

conn.commit();
rs.close();
pstm.close();
conn.close();
  • PreparedStatement在executeBatch后通过getGeneratedKeys来返回生成的主键。


mybatis自增主键


    INSERT  INTO imc_user (`user_nick`) VALUES (#{userNick})

  • mybatis的自增主键需要的配置如上所示,核心关键字包括useGeneratedKeys和keyProperty。


DefaultSqlSession

public class DefaultSqlSession implements SqlSession {

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

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      // 通过CachingExecutor执行update操作
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}
  • mybaits的insert/update都是通过DefaultSqlSession的update来实现的。
  • DefaultSqlSession的update执行CachingExecutor的update操作。


CachingExecutor

public class CachingExecutor implements Executor {

  public int update(MappedStatement ms, Object parameterObject) throws SQLException {
    flushCacheIfRequired(ms);
    return delegate.update(ms, parameterObject);
  }
  • CachingExecutor调用SimpleExecutor的update操作执行逻辑。


SimpleExecutor

public abstract class BaseExecutor implements Executor {

  @Override
  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);
  }
}

public class SimpleExecutor extends BaseExecutor {

  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // handler为PreparedStatementHandler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      // 生成Statement对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      // 执行Statement的操作
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }
}
  • SimpleExecutor的doUpdate负责执行真正的update操作。
  • 由PreparedStatementHandler来处理真正的update操作。


PreparedStatementHandler

public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  @Override
  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行PreparedStatement的操作
    ps.execute();
    // 获取影响的行数
    int rows = ps.getUpdateCount();
    // 获取KeyGenerator对象,处理自增主键
    Object parameterObject = boundSql.getParameterObject();
    // mysql使用Jdbc3KeyGenerator来进行处理
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }
  • 本质上和mysql的交互都是由PreparedStatement的execute来实现的。


Jdbc3KeyGenerator

public class Jdbc3KeyGenerator implements KeyGenerator {

  public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator();

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

  public void processBatch(MappedStatement ms, Statement stmt, Collection parameters) {
    ResultSet rs = null;
    try {
      // 获取自增主键的ResultSet集合
      rs = stmt.getGeneratedKeys();
      final Configuration configuration = ms.getConfiguration();
      final TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
      final String[] keyProperties = ms.getKeyProperties();
      final ResultSetMetaData rsmd = rs.getMetaData();
      TypeHandler[] typeHandlers = null;
      if (keyProperties != null && rsmd.getColumnCount() >= keyProperties.length) {
        for (Object parameter : parameters) {
          // there should be one row for each statement (also one for each parameter)
          if (!rs.next()) {
            break;
          }
          final MetaObject metaParam = configuration.newMetaObject(parameter);
          if (typeHandlers == null) {
            typeHandlers = getTypeHandlers(typeHandlerRegistry, metaParam, keyProperties, rsmd);
          }
          populateKeys(rs, metaParam, keyProperties, typeHandlers);
        }
      }
    } catch (Exception e) {
    } finally {
    }
  }

  private void populateKeys(ResultSet rs, MetaObject metaParam, String[] keyProperties, TypeHandler[] typeHandlers) throws SQLException {
    for (int i = 0; i < keyProperties.length; i++) {
      String property = keyProperties[i];
      TypeHandler th = typeHandlers[i];
      if (th != null) {
        Object value = th.getResult(rs, i + 1);
        metaParam.setValue(property, value);
      }
    }
  }

}
 
 
  • Jdbc3KeyGenerator在mybatis中负责处理mysql的自增主键。
  • PreparedStatement的getGeneratedKeys负责获取自增的key。
  • populateKeys根据不同数据类型对应的handler完成数据的处理。


参考文章

  • 源码参考
  • mybatis官网介绍
  • 深入理解mybatis原理
  • Mybatis3.4.x技术内幕
  • mybatis 3.x源码深度解析与最佳实践

你可能感兴趣的:(mybatis-3.4.6 自增主键)