收集Map.Entry和Map.entrySet()时出现MyBatis错误

时间:2016-03-01 08:02:06

标签: java dictionary mybatis

假设我们有MyBatis 3.3.0和MyBatis-Spring 1.2.3以及一个简单的选择查询......

<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>

(如果给定的id是1,并且集合中给出的所有关键属性都是&#34; a&#34;以及所有值&#34; b&#34;)

...这是一个简单的TestMapper接口方法......

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

......我们用这种测试方法测试它......

@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);

}

...我们会收到以下错误....

  

对于属性的参数映射,类型处理程序为null   &#39; __ frch_item_0.value&#39 ;.它没有指定和/或不能   找到指定的javaType / jdbcType组合。

原因似乎是对item.value的调用导致调用String本身的value属性。不幸的是,我不知道为什么。

entries.entrySet()替换为Collection个自定义Entry个对象(keyvalue属性)可以正常工作。同样奇怪:这似乎只发生在<collection>内,直接将Map.Entry作为参数,例如......

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

...和...

@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);

}

...作品。

有谁知道Map.EntrySet的问题是什么?有机会以某种方式修复它吗?当然,创建一个变通方法很容易,但不应该需要它。

2 个答案:

答案 0 :(得分:2)

似乎正确处理此问题的方法(文档更新已提交)如下(因为开发人员在几个版本之前做了一些更改):

<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>

原因是,<foreach> / IterablesArrays(以及Maps个对象Iteratable<Map.Entry>的行为略有不同:

  • 对于IterableArrayindex是当前迭代的编号,item是在此迭代中从Iterable中检索的元素。
  • 对于Map(或Iterable<Map.Entry>index,当前条目的密钥和项目是当前条目的value

这解释了为什么item.value导致Map<String, String>实际导致String.value value已经是String的原因char array为私有value }成员名为String.value,因此MyBatis正在尝试访问char array - 并失败,因为ret = objhttp.Open("POST", sURL, False) objhttp.setRequestHeader "Content-Type", "application/json" objhttp.setRequestHeader "Accept", "application/json" 'V3 API uses HTTP Basic Authorisation inside an https: wrapper. 'The standard windows method does not seem to work however the 'following hack does. 'In summary the user name and APIkey are seperated with a Colon: and 'base 64 encoded and added to a Http RequestHeader objhttp.setRequestHeader "Authorization", "Basic " & Base64Encode(APIUser & ":" & ApiKey) objhttp.send (sJson) 不是映射类型。)

答案 1 :(得分:0)

您只需传递参数贴图而不是map.entrySet(),就像这样

parameters.put("properties", entries);

并像那样打电话给你的mybatis

<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>