最近有这样一个需求,查询符合条件的 3D图纸包 的信息以及该图纸包里包含的图纸的信息,这显然是查询一个对象的子属性,于是有了本文,本文将讲述 mybatis 子查询,即 return a list inside an object from mybatis.
mybatis 子查询主要用到 collection 属性,如下:
<resultMap id="pageResultMap" type="xxx.xxx.xxx.xxx"> <id column="id" jdbcType="VARCHAR" property="id"/> <result column="title" jdbcType="VARCHAR" property="title"/> <result column="image" jdbcType="VARCHAR" property="image"/> <collection property="paperList" javaType="java.util.List" ofType="java.util.Map" column="{packageId=id}" select="paperList" fetchType="eager"> </collection> </resultMap>
这里,xxx.xxx.xxx.xxx 是具体的类名,关系到公司的实际业务,此处略过,下面的 ofType 也可以是实际的类名,如果是实际的类名,那么可能所有的字段将都会返回了,如果不想要返回所有的字段,可以设为 java.util.Map,下面细说下 collection 属性
property 表示这个对象的字段的名称,即子查询的结果在 xxx.xxx.xxx.xxx 里对应的变量的名称,为方便起见 下文称 xxx.xxx.xxx.xxx 为 Parent.java ,则 Parent.java 应该如下:
public class Parent { private List<HashMap<String, Object>> paperList; public List<HashMap<String, Object>> getPaperList() { return paperList; } public void setPaperList(List<HashMap<String, Object>> paperList) { this.paperList = paperList; } }
这里忽略了其他别的变量哦,主要是 变量名 paperList 要和 collection 里面的 property 对应。
javaType 表示这个子查询返回值的类型,即 paperList 的类型,这里是 java.util.List,而 ofType 指的是映射到 paperList 里面的元素的类型,这里是 java.util.Map,关键是 column,它实际是传参,你可以这么理解,column="{传入子查询的参数名 = 对应的Parent里面的字段名, ...}" ,这里传入子查询的参数名是 packageId,对应 Parent 里的 id,下面看下具体的 sql
<select id="paperList" parameterType="java.util.Map" resultType="java.util.Map"> select id as paperId, title as paperTitle, url from xxx where id in (select paperId from xxx2 where packageId = #{packageId, jdbcType=VARCHAR}) </select> <select id="findByCourseId" resultMap="pageResultMap"> select title, image, coursePackageId, id from xxx3 <if test="courseId != null"> where coursePackageId = #{courseId, jdbcType=VARCHAR} </if> order by orderNum desc <if test="start!=null and pageSize!=null">limit #{start,jdbcType=INTEGER}, #{pageSize,jdbcType=INTEGER} </if> </select>
这里 xxx 表示图纸的数据库,xxx2 是图纸和图纸包的关联数据库,xxx3 是图纸包数据库,该查询(findByCourseId)的查询结果可能如下:
{ "retCode": 1, "retMsg": "获取成功", "retObj": { "total": 1, "list": [ { "coursePackageId": "Code", "id": "PPG-190247670630060032", "image": "url", "title": "图纸包标题", "paperList": [ { "paperId": "PP-190349796114960384", "paperTitle": "title", "url": "paperLink" } ] } ] } }
当然,你可以修改 Parent.java 里的 map 为具体的类型,比如 我这里为 3D图纸的类型,然后修改 ofType 以及 resultType 为具体的类型,这样你拿到的数据类型也是对的,但是返回的数据可能有冗余,并且不支持起别名哦。总体而言,我还是觉得用 map 更好点,但是如果返回的数据你还要操作,比如读取值,那么使用具体的类型可能更好。
2019.11.04 更新 :添加 fetchType="eager" 如果不添加 可能会出现:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory$EnhancedResultObjectProxyImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: java.util.HashMap["data"]->java.util.ArrayList[0]->xxx.xxx.xxx_$$_jvst4a2_0["handler"])
如果出现这个错误,那么将
<collection property="paperList" javaType="java.util.List" ofType="java.util.Map" column="{packageId=id}" select="paperList"> </collection>
改为:
<collection property="paperList" javaType="java.util.List" ofType="java.util.Map" column="{packageId=id}" select="paperList" fetchType="eager"> </collection>
即可。