问题
在上一文介绍了 JSONObject 接受 MyBatis 的结果集的简单用法,但是在处理一对多的情况时,单纯的JSONObject就不好使了。
比如要查询一个角色下的多个用户,resultMap如下定义
1 2 3 4 5 6 7 8
| <resultMap id="roleMap" type="com.alibaba.fastjson.JSONObject"> <id column="roleId" property="roleId"/> <result column="roleName" property="roleName"/> <collection property="users" ofType="com.alibaba.fastjson.JSONObject"> <id column="userId" property="userId"/> <result column="nickname" property="nickname"/> </collection> </resultMap>
|
期望查出来的users
属性对应着一个数组,

然而实际查出来只是一个对象,只有一条数据。

解决方案
只需要建一个实体类继承 JSONObject ,里面有你要的集合类型的成员变量,就足够了。
比如我建的 One2Many 类:
1 2 3
| public class One2Many extends JSONObject { private List<JSONObject> users; }
|
然后xml改为
1 2 3 4 5 6 7 8
| <resultMap id="roleMap" type="com.heeexy.example.util.model.One2Many"> <id column="roleId" property="roleId"/> <result column="roleName" property="roleName"/> <collection property="users" ofType="com.alibaba.fastjson.JSONObject"> <id column="userId" property="userId"/> <result column="nickname" property="nickname"/> </collection> </resultMap>
|
是不是非常简单?
更棒的是,这个 One2Many 类是可以复用的,里面再添加其它的成员变量就 OK 了。而且 Dao 层不需要改动,外面正常的还是用 JSONObject 就可以了。
原理
MyBatis 在处理嵌套结果的时候,会判断这个属性的类型,如果是集合,就会初始化一个集合来接收这个属性,否则就只是一个普通的 Object 了。
什么,不满意这个答案?那就拿出源码来吧!
首先我们直接看到最底层判断这个属性是不是集合的这段源码:
DefaultResultSetHandler.instantiateCollectionPropertyIfAppropriate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| private Object instantiateCollectionPropertyIfAppropriate(ResultMapping resultMapping, MetaObject metaObject) { final String propertyName = resultMapping.getProperty(); Object propertyValue = metaObject.getValue(propertyName); if (propertyValue == null) { Class<?> type = resultMapping.getJavaType(); if (type == null) { type = metaObject.getSetterType(propertyName); } try { if (objectFactory.isCollection(type)) { propertyValue = objectFactory.create(type); metaObject.setValue(propertyName, propertyValue); return propertyValue; } } catch (Exception e) { throw new ExecutorException("Error instantiating collection property for result '" + resultMapping.getProperty() + "'. Cause: " + e, e); } } else if (objectFactory.isCollection(propertyValue.getClass())) { return propertyValue; } return null; }
|
从上面这段代码我们就知道 MyBatis 确实有做这个判断,你定义的 users 属性到底是不是集合类型,
- 如果是并且没有初始化好的话,就帮你初始化一个集合到下一步,
- 如果已经初始化好了(通常这时候就是已经塞入了几个 user 对象了),就直接返回这个 users 的值到下一步
- 如果不是集合类型,就返回 null 到下一步。
那么下一步到底是干啥呢?正常情况下应该就是继续往 users 集合里添加元素吧。
DefaultResultSetHandler.linkObjects
1 2 3 4 5 6 7 8 9 10 11
| private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) { final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); if (collectionProperty != null) { final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty); targetMetaObject.add(rowValue); } else { metaObject.setValue(resultMapping.getProperty(), rowValue); } }
|