Skip to content

There is case that a TypeHandler is incorrectly selected for a nested select query parameter #1551

Closed
@dezhishen

Description

@dezhishen

there are two table with the same column name 'ID' and without the same type

    <resultMap id="BaseResultMap" type="com.geco.domain.MdProduct">
        <id column="ID" property="id" jdbcType="VARCHAR" />
        ...
     </resultMap>
    <resultMap id="InfoResultMap" extends="BaseResultMap"  type="com.geco.service.product.dto.ProductResp">
        <result column="INFO_ID" property="additionalInfo.id" jdbcType="DECIMAL"/>
      ....
       <collection property="skus"
                    select="selectSkuByPk1" column="ID"/>
    </resultMap>
<select id="select resultMap="InfoResultMap">

   select p.id ,i.ID as INFO_ID from md_product,md_product_info i
where ....
</select>

there are error info

org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'ID' from result set.  Cause: java.sql.SQLException: Invalid value for getLong() - '82620ef6659811e9809900163e02c6cf'

I debug it and try fix it

class:  org.apache.ibatis.type.UnknownTypeHandler
method:  private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column)

old code:

private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
        try {
            Map<String, Integer> columnIndexLookup = new HashMap();
            ResultSetMetaData rsmd = rs.getMetaData();
            int count = rsmd.getColumnCount();

            for (int i = 1; i <= count; ++i) {
                String name = rsmd.getColumnName(i);
                columnIndexLookup.put(name, i);
            }

            Integer columnIndex = (Integer) columnIndexLookup.get(column);
            TypeHandler<?> handler = null;
            if (columnIndex != null) {
                handler = this.resolveTypeHandler(rsmd, columnIndex);
            }

            if (handler == null || handler instanceof UnknownTypeHandler) {
                handler = OBJECT_TYPE_HANDLER;
            }

            return (TypeHandler) handler;
        } catch (SQLException var8) {
            throw new TypeException("Error determining JDBC type for column " + column + ".  Cause: " + var8, var8);
        }
    }

this is my code:

private TypeHandler<?> resolveTypeHandler(ResultSet rs, String column) {
        try {
            Map<String, Integer> columnIndexLookup = new HashMap();
            ResultSetMetaData rsmd = rs.getMetaData();
            int count = rsmd.getColumnCount();

            for (int i = 1; i <= count; ++i) {
                String name = rsmd.getColumnLabel(i);
                columnIndexLookup.put(name, i);
            }

            Integer columnIndex = (Integer) columnIndexLookup.get(column);
            TypeHandler<?> handler = null;
            if (columnIndex != null) {
                handler = this.resolveTypeHandler(rsmd, columnIndex);
            }

            if (handler == null || handler instanceof UnknownTypeHandler) {
                handler = OBJECT_TYPE_HANDLER;
            }

            return (TypeHandler) handler;
        } catch (SQLException var8) {
            throw new TypeException("Error determining JDBC type for column " + column + ".  Cause: " + var8, var8);
        }
    }

getColumnName = > getColumnLabel

it works but i don't know there are any other bugs after i fixed this bug

Activity

harawata

harawata commented on May 31, 2019

@harawata
Member

Hi @dezhishen ,

Could you provide a demo project with a minimum set of tables/columns?
Thanks in advance!

dezhishen

dezhishen commented on Jun 22, 2019

@dezhishen
Author

@harawata
Table

create table product
(
id varchar(32) not null,
code varchar(80) not null,
name varchar(240) not null
);
create table product_sku
(
id varchar(32) not null,
product_id varchar(32) not null,
code varchar(80) not null,
color varchar(40),
size varchar(40)
);
create table product_info
(
id int(11)  not null,
product_id varchar(32) not null,
other_info varchar(240) 
);

ProductSku.class

public class ProductSku {
    private String id;
    private String color;
    private String size;
    private String productId;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }
}

ProductInfo.class

public class ProductInfo {
    private Long id;
    private String productId;
    private String otherInfo;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getProductId() {
        return productId;
    }

    public void setProductId(String productId) {
        this.productId = productId;
    }

    public String getOtherInfo() {
        return otherInfo;
    }

    public void setOtherInfo(String otherInfo) {
        this.otherInfo = otherInfo;
    }
}

ProductResp.class

public class ProductResp {
    private String id;
    private String code;
    private String name;
    private List<ProductSku> skus;
    private ProductInfo productInfo;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<ProductSku> getSkus() {
        return skus;
    }

    public void setSkus(List<ProductSku> skus) {
        this.skus = skus;
    }

    public ProductInfo getProductInfo() {
        return productInfo;
    }

    public void setProductInfo(ProductInfo productInfo) {
        this.productInfo = productInfo;
    }
}

ProductMapper.xml

<mapper namespace="ProductMapper">
   <resultMap id="AllInfoResultMap" type="ProductResp">
        <id column="ID" property="id" jdbcType="VARCHAR"/>
        <result column="NAME" property="name" jdbcType="VARCHAR"/>
        <result column="CODE" property="code" jdbcType="VARCHAR"/>
        <result column="INFO_ID" property="productInfo.id" jdbcType="DECIMAL"/>
        <result column="INFO_OTHER_INFO" property="productInfo.otherInfo" jdbcType="VARCHAR"/>
        <collection property="skus"
                    select="selectSkuByPk1" column="ID"/>
    </resultMap>
    <select id="selectAllInfo" resultMap="AllInfoResultMap">
         select p.id,
                   p.code,
                   p.name,
                   i.id as INFO_ID,
                   i.other_info as INFO_OTHER_INFO
          from product p left join product_info i
              on p.id = i.product_id
         where p.code = #{code,jdbcType=VARCHAR}
     </select>
    <resultMap id="SkuResultMap" type="ProductSku">
        <id column="ID" property="id" jdbcType="VARCHAR"/>
        <result column="PRODUCT_ID" property="productId" jdbcType="VARCHAR"/>
        <result column="COLOR" property="color" jdbcType="VARCHAR"/>
        <result column="SIZE" property="size" jdbcType="VARCHAR"/>
    </resultMap>
     <select id="selectSkuByPk1" resultMap="SkuResultMap">
          select * from product_sku
           where product_id = #{id,jdbcType=VARCHAR}
     </select>
</mapper>

ProductMapper.class

public interface ProductMapper {
    List<ProductResp> selectAllInfo(@Param("code") String code);
}  

Call function selectAllInfo

 <collection property="skus"
                    select="selectSkuByPk1" column="ID"/>

This code will use ID when there are two column that their names are ID without same type.

harawata

harawata commented on Jun 22, 2019

@harawata
Member

Thanks for the update, @dezhishen !
I think I understood the problem.

Actually, MyBatis should check the value of useColumnLabel setting.

  • when useColumnLabel=true (default), getColumnLabel() should be used.
  • when useColumnLabel=false, getColumnName() should be used.

I am not sure how to fix this cleanly yet.

huangdx0726

huangdx0726 commented on Jul 3, 2019

@huangdx0726
Contributor

@dezhishen @harawata mysql驱动包5.1及以上会产生这个问题。按你的修改可以解决这个问题,如果怕引发其他bug可以改用5.0的驱动包试试。

dezhishen

dezhishen commented on Jul 10, 2019

@dezhishen
Author

@dezhishen @harawata mysql驱动包5.1及以上会产生这个问题。按你的修改可以解决这个问题,如果怕引发其他bug可以改用5.0的驱动包试试。

通过另外一种 <collection> 使用方式和sql关联,一次查出所有数据的方式,可以避免上述情况发生,也更好
http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html#Result_Maps
在resultMap中使用

<resultMap id="detailedBlogResultMap" type="Blog">
 ...
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
     ...
  </collection>
    ...
</resultMap>

There are another way to use the tag <collection>.
We can query all columns only once call sql.
Like this:
http://www.mybatis.org/mybatis-3/sqlmap-xml.html#Result_Maps

resultMap:

<resultMap id="detailedBlogResultMap" type="Blog">
 ...
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
     ...
  </collection>
    ...
</resultMap>
kazuki43zoo

kazuki43zoo commented on Jul 10, 2019

@kazuki43zoo
Member

@huangdx0726 @dezhishen , Please use English as much as possible.

added this to the 3.5.3 milestone on Jul 14, 2019
modified the milestones: 3.5.3, 3.5.4 on Oct 20, 2019
added a commit that references this issue on Oct 24, 2019
52e4358

8 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

    Development

    Participants

    @harawata@kazuki43zoo@dezhishen@huangdx0726

    Issue actions

      There is case that a TypeHandler is incorrectly selected for a nested select query parameter · Issue #1551 · mybatis/mybatis-3