在使用mybatis时,最常使用的就是接口形式,尤其是和springmvc整合后。这种方式使用比较简单。
通过上篇mybatis通过接口查找对应的mapper.xml以及方法执行
可以了解到接口到mapper.xml的过程。
那么在mapper.xml中如何与接口中的参数对应起来呢?
比如:
public interface RedPacketDao {
List<edpacketPO> getRedpacktList(@Param("id")Long id,@Param("statusList")List<Integer> statusList);
}
<select id="getRedpacktList" resultType="Redpacket">
SELECT id, price, status, receive_time
from redpacket
where redpacket_id = #{id}
and status in
<foreach collection="statusList" item="status" open="(" separator="," close=")">
#{status}
</foreach>
</select>
对于参数id和statusList,mapper.xml是如何识别的?
之前我们了解到在方法的执行时调用MapperProxy.java中的invoke()方法。
这个方法会调用MapperMethod.java中MapperMethod.execute(sqlSession, args);
在这个方法中会解析参数:
Object param = method.convertArgsToSqlCommandParam(args);
public Object convertArgsToSqlCommandParam(Object[] args) {
final int paramCount = params.size();
//参数为空或者参数个数为0
if (args == null || paramCount == 0) {
return null;
//如果没有使用@param注解并且只有一个参数
} else if (!hasNamedParameters && paramCount == 1) {
return args[params.keySet().iterator().next().intValue()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
//参数为多个时,对params进行遍历
for (Map.Entry<Integer, String> entry : params.entrySet()) {
//如果使用注解,entry.getValue()则是注解的值,否则则是顺序的序号
param.put(entry.getValue(), args[entry.getKey().intValue()]);
// issue #71, add param names as param1, param2...but ensure backward compatibility
final String genericParamName = "param" + String.valueOf(i + 1);
//存储参数key为param1,param2的形式
if (!param.containsKey(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
这里面有个params,params的获取如下:
private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) {
final SortedMap<Integer, String> params = new TreeMap<Integer, String>();
final Class<?>[] argTypes = method.getParameterTypes();
//按照参数顺序遍历
for (int i = 0; i < argTypes.length; i++) {
if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {
//先赋值为参数的顺序号
String paramName = String.valueOf(params.size());
if (hasNamedParameters) {
//如果使用注解则赋值为注解值
paramName = getParamNameFromAnnotation(method, i, paramName);
}
//放入params中
params.put(i, paramName);
}
}
return params;
}
private String getParamNameFromAnnotation(Method method, int i, String paramName) {
final Object[] paramAnnos = method.getParameterAnnotations()[i];
for (Object paramAnno : paramAnnos) {
if (paramAnno instanceof Param) {
//获取注解名称
paramName = ((Param) paramAnno).value();
break;
}
}
return paramName;
}
所以params是Map<Integer, String>类型,根据接口方法中参数的顺序记录参数的定义名字,如果使用@param注解,就会把注解的值作为params的value,否则使用序号作为params的value。
例子如下:
void update(String name,Long id,@Param("password") String password);
生成的params如下:
{
0:'0',
1:'1',
2:"password"
}
对于参数的解析请参见上面代码上的注释。
鉴于我们可能会在sql语句中使用动态sql,所以有多个参数时做好使用@params注解,否则可能会出现值的混乱。