1、背景
最近在使用MyBatis自定义的TypeHandler在对数据做基础加工,在对ResultMap使用自定义TypeHandler时是没有问题的,可以正常进入TypeHandler中处理数据,但是当结果集被定义为ResultType时总是不进入自定义的TypeHandler,基于这个情况,不得不再次打开MyBatis的源码一探究竟
2、基础代码
/**
* 自定义TypeHandler
*
* @author fulibao
* @version 1.0
* @created 2017/7/10 下午4:29
**/
public class SecurityStringVarcharTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
//自定义代码
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
//自定义代码
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
//自定义代码
return cs.getString(columnIndex);
}
}
MyBatis XML配置文件:
<select id="getByApplicationIds" parameterType="list" resultType="censorModel">
<include refid="getAll"/>
where application_id in
<foreach collection="list" item="applicationId" index="index"
open="(" close=")" separator=",">
#{applicationId}
</foreach>
</select>
mybatis-config.xml中对于typeHandler的配置
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="java.lang.String"
handler="com.meituan.fd.crm.common.typehandler.SecurityStringVarcharTypeHandler"/>
</typeHandlers>
3、问题排查
3.1、MyBatis执行查询等操作的基础类为:DefaultSqlSession,其代码为:
public <T> T selectOne(String statement) {
return this.<T>selectOne(statement, null);
}
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
return this.selectMap(statement, null, mapKey, RowBounds.DEFAULT);
}
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
return this.selectMap(statement, parameter, mapKey, RowBounds.DEFAULT);
}
public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
final List<?> list = selectList(statement, parameter, rowBounds);
final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
configuration.getObjectFactory(), configuration.getObjectWrapperFactory());
final DefaultResultContext context = new DefaultResultContext();
for (Object o : list) {
context.nextResultObject(o);
mapResultHandler.handleResult(context);
}
Map<K, V> selectedMap = mapResultHandler.getMappedResults();
return selectedMap;
}
public <E> List<E> selectList(String statement) {
return this.selectList(statement, null);
}
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
可以看到无论是selectOne、selectMap、selectList最终调用的都是selectList方法,因此我们由selectList入手开始排查问题。
public &