简体   繁体   中英

MyBatis Error on Collection of Map.Entry and Map.entrySet()

assuming we have MyBatis 3.3.0 and MyBatis-Spring 1.2.3 and a simple select query...

<select id="testSelect" parameterType="map" resultType="Integer">
    select 1 from dual where
    <foreach collection="properties" index="index" item="item" separator=" and ">
        1 = #{id} AND 'a' = #{item.key,jdbcType=VARCHAR} AND 'b' = #{item.value,jdbcType=VARCHAR}
    </foreach>
</select>

(should simple return 1, if the id given is 1 and all key properties given in the collection are "a" and all values "b")

...which makes a simple TestMapper interface method...

Integer testSelect(Map<String, Object> arguments);

...and we test it with this test method...

@Test
public void test_for_bug() {

    final Map<String, Object> parameters = new HashMap<>();
    parameters.put("id", 1);

    final Map<String, String> entries = new HashMap<>();
    entries.put("a", "b");
    parameters.put("properties", entries.entrySet());

    final Integer result = this.testMapper.testSelect(parameters);

    assertThat(result).isEqualTo(1);

}

...we will get the following error....

Type handler was null on parameter mapping for property '__frch_item_0.value'. It was either not specified and/or could not be found for the javaType / jdbcType combination specified.

The reason for that seems to be that the call to item.value results in a call of the value property of the String itself. Unfortunately, I have no clue, why.

Replacing the entries.entrySet() with a Collection of custom Entry objects (with key and value property) works fine. Also strange: This only seems to happen inside a <collection> , giving a Map.Entry directly as a parameter, like...

<select id="testSelect" parameterType="map" resultType="Integer">
    select 1 from dual where 'b' = #{entry.value,jdbcType=VARCHAR}
</select>

...and...

@Test
public void test_for_bug() {

    final Map<String, String> entries = new HashMap<>();
    entries.put("a", "b");

    final Map<String, Object> parameters = new HashMap<>();
    parameters.put("entry", entries.entrySet().iterator().next());

    final Integer result = this.testMapper.testSelect(parameters);

    assertThat(result).isEqualTo(1);

}

...works.

Has anyone an idea what the problem with Map.EntrySet is here? Any chance to get it fixed somehow? Of course creating a workaround is easy enough, but imho it should not be needed.

Seems that the correct way to handle this (documentation update already submitted) is the following (since the developers made some changes a few versions ago):

<select id="testSelect" parameterType="map" resultType="Integer">
    select 1 from dual where
    <foreach collection="properties" index="index" item="item" separator=" and ">
        1 = #{id} AND 'a' = #{index,jdbcType=VARCHAR} AND 'b' = #{item,jdbcType=VARCHAR}
    </foreach>
</select>

The reason is, that <foreach> behaves a little bit different for Iterables / Arrays and Maps (and Iteratable<Map.Entry> Objects):

  • For an Iterable or Array , index is the number of the current iteration and item is the element retrieved from the Iterable in this iteration.
  • For a Map (or Iterable<Map.Entry> ) index is the current entry's key and item is the current entry's value

This explains why item.value for a Map<String, String> leads actually to String.value (the value is already a String - which has a private char array member called value , so MyBatis is trying to access String.value - and fails, because a char array is not a mapped type).

You just pass parameter map instead of map.entrySet(), like this

parameters.put("properties", entries);

and call your mybatis like that

<select id="testSelect" parameterType="map" resultType="Integer">
    select 1 from dual where
    <foreach collection="properties.entrySet()" index="index" item="item" separator=" and ">
        1 = #{id} AND 'a' = #{item.key,jdbcType=VARCHAR} AND 'b' = #{item.value,jdbcType=VARCHAR}
    </foreach>
</select> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM