Mybatis:There is no getter for property named 'cityName' in 'class java.lang.String'

1、问题说明

记录一个Mybatis异常:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘cityName’ in ‘class java.lang.String’

2、问题代码

若工程中是这样写mapper和配置xml文件的话,就会提示上述异常:

List<XXX> selectXXXList(String cityName);
<select id="selectXXXList" parameterType="java.lang.String" resultMap="cityMap">
        SELECT *
        FROM city
        <trim prefix="WHERE" prefixOverrides="AND">
            <if test="cityName != null">
                AND city_name LIKE concat('%',#{cityName},'%')
            </if>
            AND enabled=1
        </trim>
        ORDER BY city_id
    </select>

3、解决方法

先关注如何解决,两种方式可以解决:

方式一:mapper接口文件中,添加Mabatis标注,如下

List<XXX> selectXXXList(@Param(value="cityName") String cityName);

方式二:xml配置文件中,if条件判断条件中的参数cityName 改为 “_parameter”,如下

<select id="selectXXXList" parameterType="java.lang.String" resultMap="cityMap">
        SELECT *
        FROM city
        <trim prefix="WHERE" prefixOverrides="AND">
            <if test="_parameter != null">
                AND city_name LIKE concat('%',#{cityName},'%')
            </if>
            AND enabled=1
        </trim>
        ORDER BY city_id
    </select>

4、问题说明

4.1、使用Mapper接口时参数传递方式

Mybatis在使用Mapper接口进行编程时,其实底层是采用了动态代理机制,表面上是调用的Mapper接口,而实际上是通过动态代理调用的SqlSession的对应方法,如selectOne(),有兴趣的朋友可以查看DefaultSqlSession的getMapper()方法实现,其最终会获得一个代理了Mapper接口的MapperProxy对象。MapperProxy对象在调用Mapper接口方法时会把传递的参数做一个转换,然后把转换后的参数作为入参调用SqlSession对应的操作方法(如selectOne、insert等)。转换过程可以参考MapperMethod的execute()方法实现。简单来说是以下规则:

  • 1、如果传递过来是单参数,且没有以@Param注解进行命名,则直接将单参数作为真实的参数调用SqlSession的对应方法。
  • 2、如果传递过来的不是单参数或者是包含以@Param注解进行命名的参数,则会将对应的参数转换为一个Map进行传递。具体规则如下:
  • 2.1、会把对应的参数按照顺序以param1、param2、paramN这样的形式作为Key存入目标Map中,第一个参数是param1,第N个参数是paramN。
  • 2.2、如果参数是以@Param注解命名的参数,则以@Param指定的名称作为Key存入目标Map中。
  • 2.3、如果参数不是以@Param注解命名的,则按照顺序以0、1、N这样的形式作为Key存入目标Map中,第一个参数是0,第N个参数是N。

回到问题,单参数传递且没有使用@Param注解时,遇到各类条件语句,Mybatis会去找String.cityName这个参数,这就导致了异常。

至于另外一种解决方式,xml文件中if条件参数改为_parameter ,这就与Mybatis使用的OGNL 的表达式及底层封装有关了,封装参数时,Mybatis会有一个默认key,在没有指定key时,取这个key。具体的需要查源码。不过查找github mybatis源码时,没有找到DynamicContext.java文件出处,这个文件有提到过绑定事宜。

public static final String PARAMETER_OBJECT_KEY = "_parameter";

public DynamicContext(Configuration configuration, Object parameterObject) {
  if (parameterObject != null && !(parameterObject instanceof Map)) {
    MetaObject metaObject = configuration.newMetaObject(parameterObject);
    bindings = new ContextMap(metaObject);
  } else {
    bindings = new ContextMap(null);
  }
  bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
  bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}

5、参考资料

1、Mybatis介绍之参数传递
2、http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html
3、http://copperfield.iteye.com/blog/1275338
4、MyBatis中的OGNL教程
5、深入了解MyBatis参数

猜你喜欢

转载自blog.csdn.net/loongshawn/article/details/80929227