很多开发人员之所以编写出低效的应用,有一大原因是并不理解怎样编写高效的SQL。以订单查询为例,我们经常需要查询某个用户的订单以及订单明细,并且以树形方式展现如下:
对于这种性质的功能,很多开发人员的做法是先查询主表,然后根据主表去循环子表,如下所示:
List<Department> depts = DepartmentMapper.queryDept(); for (Department dept: depts) { dept.setEmps(EmployeeMapper.queryEmp(dept.id)); }
这种做法就是典型的过程性编程思维,它不仅在更改查询条件或字段时维护性差、不支持两个表的查询条件,而且性能低下,主表有几条记录就会导致请求数据库几次,不仅应用响应时间长,服务器也耗费了更多的资源。更好的做法是一次性查询会所有符合条件的记录,然后再应用中进行拆分并组装,主流的ORM框架几乎都支持这些模式,以使用最广泛的Mybatis为例,其结果映射resultMap中的association(用于一对一和多对一)和collection(用于一对多)元素支持自动将二位结果映射为主子对象。如下所示:
<mapper namespace="chapter6.dao.DepartmentMapper"> <!-- 嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 --> <resultMap type="chapter6.Department" id="MyDept"> <id column="did" property="id"/> <result column="dept_name" property="departmentName"/> <collection property="emps" ofType="chapter6.Employee"> <id column="eid" property="id"/> <result column="last_name" property="lastName"/> <result column="email" property="email"/> <result column="gender" property="gender"/> </collection> </resultMap> <select id="queryDept" resultMap="MyDept" > SELECT d.id did, d.dept_name dept_name, e.id, e.last_name last_name, e.email email, e.gender gender FROM tbl_dept d LEFT JOIN tbl_employee e ON d.id = e.d_id </select> </mapper>
association同样可以实现相同功能,不过明细表是主表,如下所示:
<mapper namespace="com.abc.mapper.StudentMapper"> <select id="getById" parameterType="int" resultMap="studentResultMap"> select s.id s_id, s.name s_name, s.gender s_gender, s.major s_major, s.grade s_grade, t.id t_id, t.name t_name, t.gender t_gender, t.title t_title, t.research_area t_research_area from student s left join teacher t on s.supervisor_id = t.id where s.id=#{id} </select> <resultMap id="studentResultMap" type="Student"> <id property="id" column="s_id" /> <result property="name" column="s_name" /> <result property="gender" column="s_gender" /> <result property="major" column="s_major" /> <result property="grade" column="s_grade" /> <!--使用resultMap属性引用下面的教师实体映射 --> <association property="supervisor" javaType="Teacher" resultMap="supervisorResultMap" /> </resultMap> <!--教师实体映射 --> <resultMap id="supervisorResultMap" type="Teacher"> <id property="id" column="t_id" /> <result property="name" column="t_name" /> <result property="gender" column="t_gender" /> <result property="researchArea" column="t_research_area" /> <result property="title" column="t_title" /> </resultMap> </mapper>
需要注意的是,默认情况下Mapper中使用association标签,select 有大量相同的数据,此时会出现问题, 有的数据可以联查出来, 而有的不可以。