MyBatis:association和collection标签 - 分步查询
mybatis这两个标签都可以实现单步查询和分步查询,单步查询很好理解,如下:
参考
deptDaoMapper.xml
:部门对应员工(1对多的关系)<resultMap type="com.hw.entity.Dept" id="deptinfo"><!-- 如果不用resultMap则不写 --> <result column="did" property="did" /> <result column="dname" property="dname" /> <!-- mybatis中 1方配置多方 --> <collection property="per" ofType="com.hw.entity.Person"> <result column="pid" property="pid" /> <result column="pname" property="pname" /> <result column="psex" property="psex" /> <result column="skilled" property="skilled" /> <result column="degree" property="degree" /> <result column="jobtime" property="jobtime" javaType="java.sql.Date" jdbcType="DATE" /> <result column="resume" property="resume" /> <result column="filepath" property="filepath" /> </collection> </resultMap>
personDaoMapper.xml
:员工对应部门(多对一的关系)<resultMap type="com.hw.entity.Person" id="personinfo"><!-- 如果不用resultMap则不写 --> <result column="pid" property="pid" /> <result column="pname" property="pname" /> <result column="psex" property="psex" /> <result column="skilled" property="skilled" /> <result column="degree" property="degree" /> <result column="jobtime" property="jobtime" javaType="java.sql.Date" jdbcType="DATE" /> <result column="resume" property="resume" /> <result column="filepath" property="filepath" /> <!--多对一的关系, property: 指的是属性的值, javaType:指的是属性的类型 --> <association property="dept" javaType="com.hw.entity.Dept"> <result column="did" property="did" /> <result column="dname" property="dname" /> </association> </resultMap>
这时需要在
sql
语句中通过left join
进行关联查询,接着在mybatis中进行关联表对象的封装Left join 语句。分步查询则无需在sql上写
left join
,inner join
语句,只需在进行主表查询的同时,通过传入子表查询的参数,即主表字段和子表查询参数进行绑定,主表在查询时,会默认通过主表的某个字段在子表的查询语句中进行递归查询,并将子表的查询结果封装在主表对应实体类的集合对象中。这里仅介绍分步查询的用法。
1、collection分步查询 (1对多)
参考
package com.dpf.trainingPlan.entity; import com.dpf.base.PageBaseEntity; import com.dpf.person.entity.PersonEntity; import com.dpf.organizion.entity.OrganizationEntity; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @NoArgsConstructor @Data public class TrainingPlan extends PageBaseEntity { private String pname; //计划名称 private String startTime; //计划开始时间 private String endTime; //计划结束时间 private Integer organizationId; //负责部门 private Integer personnelId; //负责人 private String prequire; //计划要求 private List<TrainingPlanFile> trainingPlanFileList; //培训计划附件 private PersonEntity personEntity; //人员管理 private OrganizationEntity organizationEntity; //组织管理 }
对于
trainingPlanFileList
对象,可以通过collection
标签进行封装<mapper namespace="com.dpf.trainingPlan.dao.TrainingPlanDao"> <resultMap id="baseMap" type="com.dpf.trainingPlan.entity.TrainingPlan"> <id column="ID" property="id"/> <result column="PNAME" property="pname"/> <result column="START_TIME" property="startTime"/> <result column="END_TIME" property="endTime"/> <result column="ORGANIZATION_ID" property="organizationId"/> <result column="PERSONNEL_ID" property="personnelId"/> <result column="PREQUIRE" property="prequire"/> <result column="CREATE_BY" property="createBy"/> <result column="CREATE_DATE" property="createDate"/> <result column="CREATE_NAME" property="createName"/> <result column="UPDATE_BY" property="updateBy"/> <result column="UPDATE_DATE" property="updateDate"/> <result column="UPDATE_NAME" property="updateName"/> <association property="personEntity" select="com.dpf.person.dao.PersonDao.findPersonInfo" column="{id=PERSONNEL_ID}"/> <association property="organizationEntity" select="com.dpf.organizion.dao.OrganizationDao.findInforganization" column="{id=ORGANIZATION_ID}"/> <collection property="trainingPlanFileList" select="com.dpf.trainingPlan.dao.TrainingPlanFileDao.findTrainingPlanFile" column="{parentId=ID}"> </collection> </resultMap> </mapper>
其中
parentId
是findTrainingPlanFile
的属性值,在调用findTrainingPlanFile
之前,需要先将parentId
封装到TrainingPlanFile
对象中,如果parentId
不是关联对象的属性值,会提示There is no getter or setter for PARENT_ID
;而ID是当前表中的ID字段,表示在子查询时,用当前表的ID字段,自动去关联表中进行查询和
trainingPlanFileList
对象封装。<select id="findTrainingPlanFile" parameterType="com.dpf.trainingPlan.entity.TrainingPlanFile" resultMap="baseMap"> select * from training_plan_file where PARENT_ID = #{parentId} AND DEL_STATUS= '0' </select>
这里还要注意的是:对于
<collection>
标签的select属性,既可以使用返回值为单个关联对象的查询方法(如上),也可以使用返回值为关联对象集合的查询方法(如下):<select id="findList" parameterType="com.dpf.trainingPlan.entity.TrainingPlanFile" resultMap="baseMap"> select * from training_plan_file where DEL_STATUS= '0' <if test="parentId != null "> AND PARENT_ID = #{parentId} </if> <if test="fileUrl != null and fileUrl != '' "> AND FILE_URL = #{fileUrl} </if> <if test="fileName != null and fileName != '' "> AND FILE_NAME = #{fileName} </if> </select>
查询到结果都如下所示:
2、associaton分步查询(多对1,一对一)
参考
public class Employee { private Integer id; private String lastName; private String email; private String gender; private Department dept; // 省略setter、getter、toString方法 } public class Department { private Integer id; private String departmentName; private List<Employee> emps; }
在
EmployeeMapper.xml
中<!-- 使用association进行分步查询: 1、先按照员工id查询员工信息 2、根据查询员工信息中的d_id值去部门表查出部门信息 3、部门设置到员工中; --> <!-- id last_name email gender d_id --> <resultMap type="com.mybatis.bean.Employee" id="MyEmpByStep"> <id column="id" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> <!-- association定义关联对象的封装规则 select:表明当前属性是调用select指定的方法查出的结果 column:指定将哪一列的值传给这个方法 流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性 --> <association property="dept" select="com.mybatis.dao.DepartmentMapper.getDeptById" column="d_id"> </association> </resultMap> <!-- public Employee getEmpByIdStep(Integer id);--> <select id="getEmpByIdStep" resultMap="MyEmpByStep"> select * from tbl_employee where id=#{id} </select>
其中部门表字段
d_id
的作用是与员工关联表的id字段进行绑定,填入到占位符#{id}
进行查询
3、小总结
- mybatis在resultmap手动映射中(假设resultMap的id为baseMap),通过collection标签(在sql上join关联查询/分步查询)可以实现在对主表查询的同时,完成对子表的查询,并将子表的查询结果封装在主表的对象集合中(前提是在查询的方法中,返回类型,即resultMap类型为baseMap,它才会自动封装)。
- 这样的关联查询往往和业务相关,如果前端页面只需要展示主表信息即可,则无需配置这个collection标签。
- 这两个标签的作用都是自动将该实体类A对应的数据表的关联表的查询结果,封装到该关联表对应的实体类对象B中,实体类A通过对象B这个property, 扩充了它向外界展示的信息。
- 对于关联查询,有两种实现方式:一种是在mybatis数据库层面进行关联查询(inner join或者分步查询),在主表中查询完一条数据之后,关联查询相关数据;
另一种是在service层面进行关联查询,这时对主表的所有查询结果进行依次遍历,接着再根据每个对象某个属性值去查询相关关联数据,并封装到该对象的关联对象中。
对于第一种方法,对数据库的负载要求较高,而对于第二种方法,对服务器的负载要求较高。- 如果在关联查询时,主表要查询的关联表的实体类不在自己开发的模块中,并且该关联类没有提供接口(无法生成代理类),使得我们无法在主表的实体类上创建该关联表对象,因此在mybatis层上关联查询会存在问题。因此需要通过调用关联表对象的service层接口实现查询,在自己的service层上实现关联查询。