Mybatis ResultMap复合映射使用以及源码分析
Posted wei_zw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis ResultMap复合映射使用以及源码分析相关的知识,希望对你有一定的参考价值。
我们知道在mybatis中可以针对一列值作为入参进行嵌套查询,那么如果入参为多个时该如何处理呢? mybatis支持复合映射,下面通过示例代码看看复合映射的使用
<resultMap id="postLiteMap2NestedWithSelect" type="org.apache.ibatis.domain.blog.BlogLite"> <id column="blog_id" property="id" /> <collection property="posts" ofType="org.apache.ibatis.domain.blog.PostLite"> <constructor> <arg javaType="org.apache.ibatis.domain.blog.PostLiteId" column="{id=id}" select="selectPostLiteId" /> <arg javaType="_int" column="blog_id"/> </constructor> </collection> </resultMap> <mapper namespace="org.apache.ibatis.domain.blog.mappers.PostMapper"> <resultMap id="postLiteIdMap" type="org.apache.ibatis.domain.blog.PostLiteId"> <constructor> <idArg javaType="_int" column="id"/> </constructor> </resultMap> <select id="selectPostLite2NestedWithSelect" resultMap="postLiteMap2NestedWithSelect"> select id, 1 as blog_id from post where blog_id is not null </select> <select id="selectPostLiteId" resultMap="postLiteIdMap"> select ${id} as id from (values(0)) as t </select>
查询
List<BlogLite> posts = session.selectList("org.apache.ibatis.domain.blog.mappers.PostMapper.selectPostLite2NestedWithSelect");
这是怎样的一个处理过程呢?下面看看时序图
下面通过代码看看Mybatis是如何处理的
public ResultMapping buildResultMapping( Class<?> resultType, String property, String column, Class<?> javaType, JdbcType jdbcType, String nestedSelect, String nestedResultMap, String notNullColumn, String columnPrefix, Class<? extends TypeHandler<?>> typeHandler, List<ResultFlag> flags, String resultSet, String foreignColumn, boolean lazy) { // Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType); //类型处理器 TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); //解析混合列 List<ResultMapping> composites = parseCompositeColumnName(column); //构建ResultMapping return new ResultMapping.Builder(configuration, property, column, javaTypeClass) .jdbcType(jdbcType) //对嵌套查询ID进行namespace处理 .nestedQueryId(applyCurrentNamespace(nestedSelect, true)) //对嵌套ResultMap进行namespace处理 .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)) .resultSet(resultSet) .typeHandler(typeHandlerInstance) .flags(flags == null ? new ArrayList<ResultFlag>() : flags) .composites(composites) .notNullColumns(parseMultipleColumnNames(notNullColumn)) .columnPrefix(columnPrefix) .foreignColumn(foreignColumn) .lazy(lazy) .build(); } private List<ResultMapping> parseCompositeColumnName(String columnName) { List<ResultMapping> composites = new ArrayList<ResultMapping>(); //如果columnName不为null 同时colunmnName中含有"=" 或者含有","号 if (columnName != null && (columnName.indexOf(‘=‘) > -1 || columnName.indexOf(‘,‘) > -1)) { //分割字符串 StringTokenizer parser = new StringTokenizer(columnName, "{}=, ", false); while (parser.hasMoreTokens()) { //获取属性 String property = parser.nextToken(); //获取列 String column = parser.nextToken(); //构建复合的ResultMapping ResultMapping complexResultMapping = new ResultMapping.Builder( configuration, property, column, configuration.getTypeHandlerRegistry().getUnknownTypeHandler()).build(); composites.add(complexResultMapping); } } return composites; }
这样得到的结果是:
至此可以发现{id=id}被解析为了一个复合resultMapping那么在使用的时候又是如何处理的呢?
在DefaultResultSetHandler中对于构造方法中的嵌套查询处理如下,如果配置的是复合映射在处理复合映射的内部映射
//获取嵌套查询构造参数的值 private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException { //嵌套查询ID final String nestedQueryId = constructorMapping.getNestedQueryId(); //嵌套查询MappedStatement final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId); //嵌套查询参数类型 final Class nestedQueryParameterType = nestedQuery.getParameterMap().getType(); //获取嵌套查询入参值 final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix); Object value = null; if (nestedQueryParameterObject != null) { final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject); final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql); final Class targetType = constructorMapping.getJavaType(); final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql); value = resultLoader.loadResult(); } return value; } private Object prepareParameterForNestedQuery(ResultSet rs, ResultMapping resultMapping, Class parameterType, String columnPrefix) throws SQLException { //如果是复合映射 if (resultMapping.isCompositeResult()) { return prepareCompositeKeyParameter(rs, resultMapping, parameterType, columnPrefix); } else { return prepareSimpleKeyParameter(rs, resultMapping, parameterType, columnPrefix); } } private Object prepareCompositeKeyParameter(ResultSet rs, ResultMapping resultMapping, Class parameterType, String columnPrefix) throws SQLException { //创建参数对象 final Object parameterObject = instantiateParameterObject(parameterType); final MetaObject metaObject = configuration.newMetaObject(parameterObject); boolean foundValues = false; //遍历复合结果映射 for (ResultMapping innerResultMapping : resultMapping.getComposites()) { //获取参数类型 final Class propType = metaObject.getSetterType(innerResultMapping.getProperty()); //获取类型处理器 final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propType); //获取值 final Object propValue = typeHandler.getResult(rs, prependPrefix(innerResultMapping.getColumn(), columnPrefix)); // 如果参数值不为null则设置到参数对象 if (propValue != null) { metaObject.setValue(innerResultMapping.getProperty(), propValue); foundValues = true; } } return foundValues ? parameterObject : null; }
此时获取到的入参 id值为1 ,同样在其它嵌套查询中也可以使用复合映射
<resultMap id="addressMapper" type="org.apache.ibatis.submitted.column_prefix.Address"> <constructor> <idArg column="id" javaType="int" /> <arg column="state" javaType="string" /> </constructor> <result property="city" column="city" /> <result property="hasPhone" column="has_phone" /> <association property="stateBird" select="selectStateBird" column="state" /> <association property="zip" select="selectZip" column="{state=state,city=city}" /> <association property="phone1" select="selectPhone" column="phone1_id" /> <association property="phone2" select="selectPhone" column="phone2_id" /> <discriminator column="addr_type" javaType="int"> <case value="1" resultType="org.apache.ibatis.submitted.column_prefix.AddressWithCaution"> <result property="caution" column="caution" /> </case> </discriminator> </resultMap>
以上是关于Mybatis ResultMap复合映射使用以及源码分析的主要内容,如果未能解决你的问题,请参考以下文章
mybatis 中 sql 映射文件 select 标签以及 入 resultMap 标签的应用