mybatis参数Thereisnogetterforproperty…异常问题
问题
在开发中遇到了There is no getter for property…异常。然后看了下源码,分析了下原因,到底什么时候加 @Param,什么时候不加。代码
mapper接⼝⽅法:
int selectStudentById(String id);
当xml⽂件如下时:
<select id="selectStudentById" parameterType="String" resultMap="StudentResult">
select *from table where
<if test ="id != null and id != '' ">
id = #{id}
</if>
</select>
会出现异常错误,应该把mapper接⼝改为:
int selectStudentById(@Param("id") String id);
总结:
当 xml 的 paramType 属性为 Map 时,接⼝中不需要加 @Param 注解。
当 xml 的 paramType 属性为 String ,Integer时,且xml中使⽤ < if > 标签,接⼝中需要加 @Param 注解。
当接⼝中只有⼀个属性,且xml中未使⽤ < if > 标签,不⽤加 @Param 标签。
当接⼝中有多个参数时,必须加⼊ @Param 标签。
源码分析:
下⾯篇幅较长,请耐⼼阅读。
mapper接⼝两种不同的⽅法:
public Expert selectExpertById(@Param("id") String id);
public Expert selectExpertById(String id);
xml⽅法实现:
<select id="selectExpertById" parameterType="java.lang.String" resultMap="ExpertResult">
select * from tbpm_expert where
<if test="id != null and id != '' ">
id = #{id}
</if>
</select>
1. ⾸先会调⽤mybatis-3.4.0.jar中的 MapperProxy 代理类,调⽤MapperProxy类中的 invoke ⽅法。
//method:mapper中的接⼝⽅法;args:mapper中的参数值
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.DeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
ute(sqlSession, args);
}
2. 之后会调⽤ MapperMethod 的 excute ⽅法, sqlCommandType 表⽰ xml 中的查询类型,是 select 还是 insert,此次查询返回
单个实体类,继续调⽤类中的 convertArgsToSqlCommandParam ⽅法,把参数转化为 sql 语句需要的格式。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//SqlCommandType表⽰xml中的查询类型
if (SqlCommandType.INSERT == Type()) {
Object param = vertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.Name(), param));
} else if (SqlCommandType.UPDATE == Type()) {
Object param = vertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.Name(), param));
} else if (SqlCommandType.DELETE == Type()) {
Object param = vertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.Name(), param));
} else if (SqlCommandType.SELECT == Type()) {
//继续判断返回值类型
if (urnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (urnsMany()) {
result = executeForMany(sqlSession, args);
} else if (urnsMap()) {
result = executeForMap(sqlSession, args);
} else if (urnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
//此次查询返回单个实体类
Object param = vertArgsToSqlCommandParam(args);
result = sqlSession.Name(), param);
}
//省略⽆关代码
return result;
}
3. mapper参数有 @Param 从 method 中获取到参数信息,进⾏封装,返回Map对象,此时param为 {id:id值,param1:id值}。此
时需要注意hasNamedParameters 参数,表⽰是否包含命名参数,即 mapper 接⼝中有没有加@Param。
没有 @Param 属性,并且 参数个数为1,返回mapper接⼝中的参数值,此时 param 是⼀个 String 对象。error parse new
public Object convertArgsToSqlCommandParam(Object[] args) {
//params是method中属性,此次查询参数是id
final int paramCount = params.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasNamedParameters && paramCount == 1) {
//mapper中没有@Param属性,并且参数个数为1,返回参数值
return args[params.keySet().iterator().next().intValue()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : Set()) {
param.Value(), Key().intValue()]);
// issue #71, add param names as param1, but ensure backward compatibility
final String genericParamName = "param" + String.valueOf(i + 1);
if (!ainsKey(genericParamName)) {
param.put(genericParamName, Key()]);
}
i++;
}
return param;
}
}
4. 回到 excute ⽅法中,继续往下⾛到 sqlSession.Name(),param),之后进⾏⼀系列跳转,进⼊
DynamicSqlSource 的 getBoundSql ⽅法,parameterObject 为 selectOne 中的param。
⾸先 new ⼀个 DynamicContext 对象,⾥⾯包含⼀个 bindings 属性,是⼀个Map,封装着参数 _parameter 和设置的数据库类型
_dataBases, _dataBases是 mybatis 数据源的类型(例如mysql)。
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : Class();
SqlSource sqlSource = sqlSourceParser.Sql(), parameterType, Bindings());
BoundSql boundSql = BoundSql(parameterObject);
for (Map.Entry<String, Object> entry : Bindings().entrySet()) {
boundSql.Key(), Value());
}
return boundSql;
}
初始化 DynamicContext 时,parameterObject 不为空并且不是⼀个 Map 对象时,new ⼀个 MetaObject 对象,把 parameterObject 保存到⾥⾯,并且作为参数,初始化⼀个 ContextMap 对象。否则初始化 ContextMap 时,参数MetaObject 为 null,后⾯会⽤到metaObject 是否为 null。
public DynamicContext(Configuration configuration, Object parameterObject) {
//parameterObject不为空并且不是map,new⼀个MetaObject对象,后⾯会⽤到
if (parameterObject != null && !(parameterObject instanceof Map)) {
MetaObject metaObject = wMetaObject(parameterObject);
bindings = new ContextMap(metaObject);
} else {
bindings = new ContextMap(null);
}
bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, DatabaseId());
}
5. 回到 DynamicSqlSource 的 getboundSql ⽅法的 rootSqlNode.apply(context),把 xml 中的 sql 语句进⾏处理,此时如果 xml
中有 < if >标签,会进⾏处理获取到其中的常量,⽰例中的常量为 id 字段。
之后会再进⼊ DynamicContext,调⽤ getProperty ⽅法,参数:target 是上⾯的 bindings,name 为上⾯的常量 id。类中重写了 get ⽅法。
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
Map map = (Map) target;
//此处重写了get⽅法
Object result = (name);
if (ainsKey(name) || result != null) {
return result;
}
Object parameterObject = (PARAMETER_OBJECT_KEY);
if (parameterObject instanceof Map) {
return ((Map) parameterObject).get(name);
}
return null;
}
6. 当⾛到 (name) 时,paramerMetaObject 即为上⾯的 metaObject,mapper接⼝中有
@Parma,parameterMetaObject 为 null,返回 null,没有 @Param 的时候,parameterMetaObject 不为 null,⾛
新⼿⼩⽩,有错误的地⽅欢迎⼤家指正,谢谢。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。