mybatis-3.4.6|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-3.4.6|mybatis-3.4.6 自增主键】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 = https://www.it610.com/article/rs.getObject(1); System.out.println(value); }conn.commit(); rs.close(); pstm.close(); conn.close();

  • PreparedStatement在executeBatch后通过getGeneratedKeys来返回生成的主键。


mybatis自增主键
INSERTINTO 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 = https://www.it610.com/article/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源码深度解析与最佳实践

    推荐阅读