插入是返回自增 id 这个功能在 jdbc 上就已经实现了,所以 mybatis 只是对其进行了封装,先研究 jdbc 返回自增主键得原理。
以下是部分 jdbc 代码
PreparedStatement pst = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 执行SQL
pst.setString(1, "qqqq");
pst.setInt(2, 15);
int count = pst.executeUpdate();
// 获取主键值
ResultSet rs = pst.getGeneratedKeys();
首先可以看到,在进行预处理得时候,就通过 Statement.RETURN_GENERATED_KEYS 来设置这次会话可以拿到自增主键。
@Override
public java.sql.PreparedStatement prepareStatement(String sql, int autoGenKeyIndex) throws SQLException {
java.sql.PreparedStatement pStmt = prepareStatement(sql);
((ClientPreparedStatement) pStmt).setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS);
return pStmt;
}
// 将 retrieveGeneratedKeys 设置为 true
protected void setRetrieveGeneratedKeys(boolean flag) throws SQLException {
synchronized (checkClosed().getConnectionMutex()) {
this.retrieveGeneratedKeys = flag;
}
}
执行完 sql 后我们看 getGeneratedKeys() 是怎么拿到主键得。
@Override
public java.sql.ResultSet getGeneratedKeys() throws SQLException {
// 还记得 retrieveGeneratedKeys 这个变量吗,加入没有设置可以拿到自增主键就直接抛出异常。
if (!this.retrieveGeneratedKeys) {
throw SQLError.createSQLException(Messages.getString("Statement.GeneratedKeysNotRequested"), MysqlErrorNumbers.SQL_STATE_ILLEGAL_ARGUMENT,
getExceptionInterceptor());
}
if (this.batchedGeneratedKeys == null) {
if (this.lastQueryIsOnDupKeyUpdate) {
return this.generatedKeysResults = getGeneratedKeysInternal(1);
}
return this.generatedKeysResults = getGeneratedKeysInternal();
}
.....
}
}
接着点下去会发现,只是通过 long beginAt = getLastInsertID(); 这行代码拿到了新增后最后的那个 id ,但是只是拿到了一个属性,那么这个 lastInsertId 属性是怎么设置进去得呢,那么就需要看
jdbc 代码中的 pst.executeUpdate(),来看看到 sql 是如何执行的。
protected long executeUpdateInternal(QueryBindings bindings, boolean isReallyBatch) throws SQLException {
....
rs = executeInternal(-1, sendPacket, false, false, null, isReallyBatch);
....
this.lastInsertId = rs.getUpdateID();
.....
}
能发现当真正执行完之后,是通过结果集的 UpdateID 来给 lastInsertId 来赋值的。当我想怎么才能查看到 updateId 是什么时候被赋值的时候。我发现后面的源码基本与是否开启自增主键无关,而是不管是否开启返回自增主键,每次执行完之后,updateId 都会是插入后的最新值。
结论
开启不管是否要求 jdbc 返回主键值,其实它每次插入后都会拿到这个值。MyBatis 也是同理,只是解析了 xml 文件,通过是否设置 useGeneratedKeys 来设置相应的连接,另外 MyBatis 可以通过 keyProperty 里面的主键名来自动帮我们进行映射。