Search code examples
javaormsybasemybatisspring-mybatis

MyBatis and Sybase Stored Procedure - ResultSet with unnamed columns


I am trying to map the ResultSet of a Sybase Stored Procedure using MyBatis, but I am experiencing some problems with unnamed columns on it. The procedure returns a single row with 4 columns, 2 of which have no name at all:

+---id---|------|-----------|---description---+
     1     name1  surname1        desc1

The resultmap .xml file looks like this (check the second and third result tags):

<resultMap id="person" type="foo.Person">
    <result column="id" property="id"/>
    <result column="" property="name"/>
    <result column="" property="surname"/>
    <result column="description" property="description"/>
</resultMap>

I have no control of the stored procedure code, so adding an alias to the unnamed columns is not a possibility. Checking the previous versions of MyBatis, I saw that in the past you could inform a columnIndex in the ResultMap, but now it seems to be no longer available. Is there some sort of custom ResultSet handler that could be used for this? Or maybe some kind of customized TypeHandler?


Solution

  • It is a workaround, but since you cannot change the procedure in the database to add the names I cannot think of another way.

    The idea is to create a type handler specific for those fields. Note that if you have a single unnamed field you can simply map it using an empty column name.

    So in your case the result map will look like this:

    <resultMap id="person" type="foo.Person">
        <result column="id" property="id"/>
        <result column="" property="name" typeHandler="foo.PersonNameTypeHandler"/>
        <result column="" property="surname"  typeHandler="foo.PersonSurnameTypeHandler"/>
        <result column="description" property="description"/>
    </resultMap>
    

    The sybase driver will return all unnamed columns as "".

    MyBatis requires you to map your columns to an existing column name or it will not call the type handler. Because of that you cannot create a fake name for these columns, so just use empty for all unnamed columns, in the end the empty name if important just to force MyBatis to process the column.

    The important part is in the type handler, it just returns the value based on the column number where "name" is supposed to be:

    public class PersonNameTypeHandler extends BaseTypeHandler<String> {
        ...
        @Override
        public String getNullableResult(ResultSet rs, String columnName) {
            return rs.getString(2);
        }
        ...
    }
    

    It is important to notice that the called method passes columnName, which in this case will be empty, so just ignore it and return by the column index.

    Also, ResultSet column index starts at 1, so in this case to get the name you get 2, for surname the implementation would be:

    ...
    return rs.getString(3);
    ...
    

    Another important detail is that the column order must be related to the return order defined in your procedure and not in your result map. If the "name" column was after the "description" column in the above result map, but the procedure return did not change, the index used in the getString would still be 2, not 4.

    Hope this helps.