对mybatis的Handler 从使用角度介绍

最近在开发中,涉及到了讲数据库查询的类型,直接转为java需要的类型。 由于对handler 理解不到位 和 使用不当。躺了一些坑。

主要涉及的有2种。

1、varchar 转 List<T>

2、varchar 转Map<T>

如图是写的两个handler。 

 ListTypeHandler 为了保证 handler的通用性,采取了 将mybatis xml 配置中的type,传入 ListTypeHandler 中直接使用。 后来发现这是个大坑。
public class ListTypeHandler<E> extends BaseTypeHandler<List<E>> {
    
    private Class<E> type;

    public ListTypeHandler(Class<E> type) {
        if (type == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.type = type;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List<E> parameter, JdbcType jdbcType) throws SQLException {
        String x = JSON.toJSONString(parameter);
        ps.setString(i, x);
    }

    @Override
    public List<E> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);
        return StringUtils.isBlank(s) ? null : JSON.parseArray(s, type);
    }

    @Override
    public List<E> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);
        return StringUtils.isBlank(s) ? null : JSON.parseArray(s, type);
    }

    @Override
    public List<E> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        return StringUtils.isBlank(s) ? null : JSON.parseArray(s, type);
    }

}
public class MapTypeHandler extends BaseTypeHandler<Map<String, Object>> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException {
        if(MapUtils.isNotEmpty(parameter)) {
            String x = JSON.toJSONString(parameter);
            ps.setString(i, x);
        }else{
            ps.setString(i, null);
        }
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String s = rs.getString(columnName);
        return StringUtils.isBlank(s) ? null : JSON.parseObject(s, new TypeReference<Map<String, Object>>(){}); // JSON.parseObject(s, getRawType());
    }

    @Override
    public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String s = rs.getString(columnIndex);
        return StringUtils.isBlank(s) ? null : JSON.parseObject(s, new TypeReference<Map<String, Object>>(){}); 
    }

    @Override
    public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String s = cs.getString(columnIndex);
        return StringUtils.isBlank(s) ? null : JSON.parseObject(s, new TypeReference<Map<String, Object>>(){}); 
    }

}

 首先没有在mybatis.xml  总注册,直接通过@result指定使用

    @Select("<script>SELECT " + SELECT_FILEDS_AS + " FROM " + TABLE + " WHERE valid =1 and config_id = #{configId} " + SELECT_LIMIT + " </script>")
    @Results(value={
            @Result(column="operation_content", property="operationContent", javaType=String.class, jdbcType=JdbcType.VARCHAR,typeHandler=ListTypeHandler.class),
    })
    List<OperateRecord> selectList(@Param("configId") long configId,@Param("offset") int offset,@Param("limit") int limit);

 

由于希望 ListTypeHandler 可以通用一点,因此采用了传入的javaType 即String,使用, 这个时候查询也没问题。感觉还很完美。

接下来,开始写插入

  @Insert({"INSERT INTO " + TABLE + " (config_id, operation_staff, operation_time, action, operation_content, ctime,valid) values "
            +"( #{configId}, #{operationStaff}, #{operationTime}, #{action}, #{operationContent, javaType=String, jdbcType=VARCHAR,typeHandler=ListTypeHandler}, unix_timestamp(),1 )"})
    int insert(OperateRecord record);

 

这个时候就开始各种报错,最后经过一顿百度,发现是没有注册,于是增加注册。

<typeHandlers>
        <typeHandler handler="com.support.ListTypeHandler" javaType="List" jdbcType="VARCHAR" />
    </typeHandlers>

 

这个时候,在插入,发现成功了。 这个时候,认为全部搞定了,开始部署线下,进行联调。结果最大的问题才刚刚开始。

后头测试查询的时候,发现查询又报错。。。 于是开始debug,发现 'name' 类型varchar,java类型 String。 也开始进入list转换。 一脸懵逼。

于是尝试性的去掉字段的别名,发现居然成功了。。。

然后开始进入另一个大坑。认为和别名有关。

进入下一步的尝试

 @Result(column = "initiateName" ,property = "initiateName")

 

为字段指定result,发现问题完美解决。 

这个时候,任务只要@Results(value={}),使用过handler的,其他字段就也要指定。

这个时候,终于认为大功告成,结果。。。。

测试另一个dao的时候,发现,这个dao完全没有使用handler, 查询结果中的字段,也会去转换。。

然后,又开始懵逼。。。 一顿百度,最终恍然大悟。 再mybatisxml配置之后,是全局匹配。

因为不能使用传入的java类型, 再listhandler写死。

结论:

1.查询时候,不需要注册,即可直接通过result使用。type指定也可以生效。

2.插入的时候必须注册。

3.注册后,通过java,jdbc类型,还有名字全局匹配

4.result中可以不写,自动匹配

 

posted @ 2018-06-25 20:10  鹊南  阅读(3556)  评论(0编辑  收藏  举报