今天在调用别人写的保存数据接口时,需要返回主键;结果一直获取不到插入记录的ID。排查了很久才发现是写法有问题
样例如下:
// mapper 接口
void saveUser(@Param("user") User user);
xml定义:
<insert id="saveUser">
<selectKey keyProperty="id" resultType="long" order="BEFORE">
select CAST(rand()*100 as SIGNED) as id
</selectKey>
insert into tf_f_user(user_id, user_name,cust_id) values (
#{id, jdbcType=BIGINT},
#{user.username, jdbcType=VARCHAR},
#{user.cust.id, jdbcType=BIGINT})
</insert>
开始没有仔细查看方法定义,直接调整selectKey的写法;各种尝试都不对,只好跟下源码。处理selectKey的源码如下:
/*
* Copyright 2009-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor.keygen;
import java.sql.Statement;
import java.util.List;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.RowBounds;
public class SelectKeyGenerator implements KeyGenerator {
public static final String SELECT_KEY_SUFFIX = "!selectKey";
private boolean executeBefore;
private MappedStatement keyStatement;
public SelectKeyGenerator(MappedStatement keyStatement, boolean executeBefore) {
this.executeBefore = executeBefore;
this.keyStatement = keyStatement;
}
public void processBefore(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
if (executeBefore) {
processGeneratedKeys(executor, ms, parameter);
}
}
public void processAfter(Executor executor, MappedStatement ms, Statement stmt, Object parameter) {
if (!executeBefore) {
processGeneratedKeys(executor, ms, parameter);
}
}
private void processGeneratedKeys(Executor executor, MappedStatement ms, Object parameter) {
try {
if (parameter != null && keyStatement != null && keyStatement.getKeyProperties() != null) {
String keyProperty = keyStatement.getKeyProperties()[0]; // just one key property is supported
final Configuration configuration = ms.getConfiguration();
final MetaObject metaParam = configuration.newMetaObject(parameter);
if (keyProperty != null && metaParam.hasSetter(keyProperty)) {
// Do not close keyExecutor.
// The transaction will be closed by parent executor.
Executor keyExecutor = configuration.newExecutor(executor.getTransaction(), ExecutorType.SIMPLE);
List<Object> values = keyExecutor.query(keyStatement, parameter, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER);
if (values.size() == 0) {
throw new ExecutorException("SelectKey returned no data.");
} else if (values.size() > 1) {
throw new ExecutorException("SelectKey returned more than one value.");
} else {
metaParam.setValue(keyProperty, values.get(0));
}
}
}
} catch (ExecutorException e) {
throw e;
} catch (Exception e) {
throw new ExecutorException("Error selecting key or setting result to parameter object. Cause: " + e, e);
}
}
}
在processGeneratedKeys方法中发现parameter是一个org.apache.ibatis.binding.MapperMethod.ParamMap(MapperMethod的内部类,继承自HashMap), 再执行metaParam.setValue()后参数没有任何变化。觉的应该是这里有问题。找到方法定义果然是使用了@param注解,导致参数变成了Map;把注解去掉调整下mapper.xml就一切正常了