1. 概述
本文,我们来分享 SQL 执行的第三部分,keygen
包。整体类图如下:
- 我们可以看到,整体是以 KeyGenerator 为核心。所以,本文主要会看到的就是 KeyGenerator 对自增主键的获取。
2. KeyGenerator
org.apache.ibatis.executor.keygen.KeyGenerator
,主键生成器接口。代码如下:
// KeyGenerator.java
public interface KeyGenerator {
// SQL 执行前 void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
// SQL 执行后 void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}
|
KeyGenerator 有三个子类,如下图所示:
3. Jdbc3KeyGenerator
org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator
,实现 KeyGenerator 接口,基于 Statement#getGeneratedKeys()
方法的 KeyGenerator 实现类,适用于 MySQL、H2 主键生成。
3.1 构造方法
// Jdbc3KeyGenerator.java
/** * A shared instance. * * 共享的单例 * * @since 3.4.3 */ public static final Jdbc3KeyGenerator INSTANCE = new Jdbc3KeyGenerator();
|
3.2 processBefore
@Override public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { // do nothing }
|
- 空实现。因为对于 Jdbc3KeyGenerator 类的主键,是在 SQL 执行后,才生成。
3.3 processAfter
// Jdbc3KeyGenerator.java
@Override public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) { processBatch(ms, stmt, parameter); }
|
- 调用
#processBatch(Executor executor, MappedStatement ms, Statement stmt, Object parameter)
方法,处理返回的自增主键。单个 parameter
参数,可以认为是批量的一个特例。
3.4 processBatch
// Jdbc3KeyGenerator.java
public void processBatch(MappedStatement ms, Statement stmt, Object parameter) { // <1> 获得主键属性的配置。如果为空,则直接返回,说明不需要主键 final String[] keyProperties = ms.getKeyProperties(); if (keyProperties == null || keyProperties.length == 0) { return; } ResultSet rs = null; try { // <2> 获得返回的自增主键 rs = stmt.getGeneratedKeys(); final Configuration configuration = ms.getConfiguration(); if (rs.getMetaData().getColumnCount() >= keyProperties.length) { // <3> 获得唯一的参数对象 Object soleParam = getSoleParameter(parameter); if (soleParam != null) { // <3.1> 设置主键们,到参数 soleParam 中 assignKeysToParam(configuration, rs, keyProperties, soleParam); } else { // <3.2> 设置主键们,到参数 parameter 中 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); } finally { // <4> 关闭 ResultSet 对象 if (rs != null) { try { rs.close(); } catch (Exception e) { // ignore } } } }
|
-
<1>
处,获得主键属性的配置。如果为空,则直接返回,说明不需要主键。
- 【重要】
<2>
处,调用 Statement#getGeneratedKeys()
方法,获得返回的自增主键。
-
<3>
处,调用 #getSoleParameter(Object parameter)
方法,获得唯一的参数对象。详细解析,先跳到 「3.4.1 getSoleParameter」 。
-
<4>
处,关闭 ResultSet 对象。
3.4.1 getSoleParameter
// Jdbc3KeyGenerator.java
/** * 获得唯一的参数对象 * * 如果获得不到唯一的参数对象,则返回 null * * @param parameter 参数对象 * @return 唯一的参数对象 */ private Object getSoleParameter(Object parameter) { // <1> 如果非 Map 对象,则直接返回 parameter if (!(parameter instanceof ParamMap || parameter instanceof StrictMap)) { return parameter; } // <3> 如果是 Map 对象,则获取第一个元素的值 // <2> 如果有多个元素,则说明获取不到唯一的参数对象,则返回 null Object soleParam = null; for (Object paramValue : ((Map<?, ?>) parameter).values()) { if (soleParam == null) { soleParam = paramValue; } else if (soleParam != paramValue) { soleParam = null; break; } } return soleParam; }
|
-
<1>
处,如下可以符合这个条件。代码如下:
@Options(useGeneratedKeys = true, keyProperty = "id") @Insert({"insert into country (countryname,countrycode) values (#{country.countryname},#{country.countrycode})"}) int insertNamedBean(@Param("country") Country country);
|
-
<2>
处,如下可以符合这个条件。代码如下:
@Options(useGeneratedKeys = true, keyProperty = "country.id") @Insert({"insert into country (countryname, countrycode) values (#{country.countryname}, #{country.countrycode})"}) int insertMultiParams_keyPropertyWithWrongParamName2(@Param("country") Country country, @Param("someId") Integer someId);
|
- 虽然有
country
和 someId
参数,但是最终会被封装成一个 parameter
参数,类型为 ParamMap 类型。为什么呢?答案在 ParamNameResolver#getNamedParams(Object[] args)
方法中。
- 如果是这个情况,获得的主键,会设置回
country
的 id
属性,因为注解上的 keyProperty = "country.id"
配置。
-