key Generator主要是用于insert操作,其接口定义为
public interface KeyGenerator {
void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter);
}
其相关类层次图为
1、keyGenerator相关的配置
全局配置useGeneratedKeys,默认是false
局部配置,对于某条sql语句,useGeneratedKeys
当局部配置没有配置时,在全局配置并且命令为insert才有效
2、 selectKey结点与相关KeyGenerator关系
selectKey主要是对于不支持自动生成主键时的处理。这种情况使用的是SelectKeyGenerator,通过调用processBefore来生成主键,而对于可以生成主键的,主要是通过processAfter来处理
//BaseStatementHandler
protected void generateKeys(Object parameter) {
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
ErrorContext.instance().store();
keyGenerator.processBefore(executor, mappedStatement, null, parameter);
ErrorContext.instance().recall();
}
3、 KeyGenerator的调用
在sql语句执行后的处理,主要是在CallableStatementHandler、 PreparedStatementHandler、 SimpleStatementHandler中的update中执行sql后的处理
// CallableStatementHandler
public int update(Statement statement) throws SQLException {
CallableStatement cs = (CallableStatement) statement;
cs.execute();
int rows = cs.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
resultSetHandler.handleOutputParameters(cs);
return rows;
}
// PreparedStatementHandler
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}
// SimpleStatementHandler
public int update(Statement statement) throws SQLException {
String sql = boundSql.getSql();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
int rows;
if (keyGenerator instanceof Jdbc3KeyGenerator) {
statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else if (keyGenerator instanceof SelectKeyGenerator) {
statement.execute(sql);
rows = statement.getUpdateCount();
keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
} else {
statement.execute(sql);
rows = statement.getUpdateCount();
}
return rows;
}
4、Jdbc3KeyGenerator的实现
其主要是针对支持主键自动生成的数据库。在处理主键时,分参数类型
(1)如果是ParaMap或者StrictMap时,对于单参数时,以Map的任意一个key作为参数名来构建 String到KeyAssigner的实体对
(2)如果参数是ArrayList并且非空,元素是ParaMap时
(3)单参数并且没有@Param注解
private void assignKeys(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd, String[] keyProperties,
Object parameter) throws SQLException {
if (parameter instanceof ParamMap || parameter instanceof StrictMap) {
// Multi-param or single param with @Param
assignKeysToParamMap(configuration, rs, rsmd, keyProperties, (Map<String, ?>) parameter);
} else if (parameter instanceof ArrayList && !((ArrayList<?>) parameter).isEmpty()
&& ((ArrayList<?>) parameter).get(0) instanceof ParamMap) {
// Multi-param or single param with @Param in batch operation
assignKeysToParamMapList(configuration, rs, rsmd, keyProperties, (ArrayList<ParamMap<?>>) parameter);
} else {
// Single param without @Param
assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);
}
}
private void assignKeysToParamMap(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
String[] keyProperties, Map<String, ?> paramMap) throws SQLException {
if (paramMap.isEmpty()) {
return;
}
Map<String, Entry<Iterator<?>, List<KeyAssigner>>> assignerMap = new HashMap<>();
for (int i = 0; i < keyProperties.length; i++) {
Entry<String, KeyAssigner> entry = getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i],
keyProperties, true);
Entry<Iterator<?>, List<KeyAssigner>> iteratorPair = assignerMap.computeIfAbsent(entry.getKey(),
k -> entry(collectionize(paramMap.get(k)).iterator(), new ArrayList<>()));
iteratorPair.getValue().add(entry.getValue());
}
long counter = 0;
while (rs.next()) {
for (Entry<Iterator<?>, List<KeyAssigner>> pair : assignerMap.values()) {
if (!pair.getKey().hasNext()) {
throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));
}
Object param = pair.getKey().next();
pair.getValue().forEach(x -> x.assign(rs, param));
}
counter++;
}
}
private Entry<String, KeyAssigner> getAssignerForParamMap(Configuration config, ResultSetMetaData rsmd,
int columnPosition, Map<String, ?> paramMap, String keyProperty, String[] keyProperties, boolean omitParamName) {
Set<String> keySet = paramMap.keySet();
// A caveat : if the only parameter has {@code @Param("param2")} on it,
// it must be referenced with param name e.g. 'param2.x'.
boolean singleParam = !keySet.contains(SECOND_GENERIC_PARAM_NAME);
int firstDot = keyProperty.indexOf('.');
if (firstDot == -1) {
if (singleParam) {
return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);
}
throw new ExecutorException("Could not determine which parameter to assign generated keys to. "
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
+ keySet);
}
String paramName = keyProperty.substring(0, firstDot);
if (keySet.contains(paramName)) {
String argParamName = omitParamName ? null : paramName;
String argKeyProperty = keyProperty.substring(firstDot + 1);
return entry(paramName, new KeyAssigner(config, rsmd, columnPosition, argParamName, argKeyProperty));
} else if (singleParam) {
return getAssignerForSingleParam(config, rsmd, columnPosition, paramMap, keyProperty, omitParamName);
} else {
throw new ExecutorException("Could not find parameter '" + paramName + "'. "
+ "Note that when there are multiple parameters, 'keyProperty' must include the parameter name (e.g. 'param.id'). "
+ "Specified key properties are " + ArrayUtil.toString(keyProperties) + " and available parameters are "
+ keySet);
}
}
private Entry<String, KeyAssigner> getAssignerForSingleParam(Configuration config, ResultSetMetaData rsmd,
int columnPosition, Map<String, ?> paramMap, String keyProperty, boolean omitParamName) {
// Assume 'keyProperty' to be a property of the single param.
String singleParamName = nameOfSingleParam(paramMap);
String argParamName = omitParamName ? null : singleParamName;
return entry(singleParamName, new KeyAssigner(config, rsmd, columnPosition, argParamName, keyProperty));
}
private void assignKeysToParamMapList(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
String[] keyProperties, ArrayList<ParamMap<?>> paramMapList) throws SQLException {
Iterator<ParamMap<?>> iterator = paramMapList.iterator();
List<KeyAssigner> assignerList = new ArrayList<>();
long counter = 0;
while (rs.next()) {
if (!iterator.hasNext()) {
throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));
}
ParamMap<?> paramMap = iterator.next();
if (assignerList.isEmpty()) {
for (int i = 0; i < keyProperties.length; i++) {
assignerList
.add(getAssignerForParamMap(configuration, rsmd, i + 1, paramMap, keyProperties[i], keyProperties, false)
.getValue());
}
}
assignerList.forEach(x -> x.assign(rs, paramMap));
counter++;
}
}
private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,
String[] keyProperties, Object parameter) throws SQLException {
Collection<?> params = collectionize(parameter);
if (params.isEmpty()) {
return;
}
List<KeyAssigner> assignerList = new ArrayList<>();
for (int i = 0; i < keyProperties.length; i++) {
assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));
}
Iterator<?> iterator = params.iterator();
while (rs.next()) {
if (!iterator.hasNext()) {
throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, params.size()));
}
Object param = iterator.next();
assignerList.forEach(x -> x.assign(rs, param));
}
}
版权声明:本文为wuli2496原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。