多对一查询
例如根据学生查班级,使用<association>标签。
关联方式查询:通过多表关联的SQL语法进行查询, 需要使用join, on, …来实现查询。执行一条SQL语句就可以将所有需要的数据全部查询到。我们需要做的就是将查到的数据进行映射即可。实体类的属性名与表中的字段名一样时,默认不会自动映射,需要设置autoMapping=“true”,即可自动映射。通过association 映射实体类中包括的其他实体类。缺点:SQL语句复杂。
<mapper namespace="multiTableQueryMapper.UserMapper">
<resultMap id="wu" type="Student" autoMapping="true">
<id property="id" column="cid"/>
<!-- <result property="name" column="name"/>-->
<!-- <result property="gender" column="gender"/>-->
<!-- <result property="birthday" column="birthday"/>-->
<association property="clazz"
javaType="Clazz" autoMapping="true">
<id property="id" column="id"/>
<!-- <result property="name" column="name"/>-->
<!-- <result property="room" column="room"/>-->
</association>
</resultMap>
<select id="selAll" resultMap="wu">
select * from tb_student s left join tb_class c on c.id=s.cid
</select>
<mapper>
N+1查询。一次查询出N个学生的信息,在根据N个学生的信息一次一次的查询N次,查出每个学生的班级信息。表示所有数据要获取到需要执行N+1条SQL语句。每次查询都是单表查询,查多次就拿到所有数据了。column是传递给select中查询id的参数。实体类的属性名与表中的字段名同名会自动映射。缺点:会对数据库多次访问。
<resultMap id="cao" type="Student">
<id property="id" column="cid"/>
<!-- <result property="name" column="name"/>-->
<!-- <result property="gender" column="gender"/>-->
<!-- <result property="birthday" column="birthday"/>-->
<association property="clazz"
javaType="Clazz"
column="cid"
select="multiTableQueryMapper.ClazzMapper.selById"/>
<!--JavaType为查询到的返回实体类型-->
</resultMap>
<select id="selAll2" resultMap="cao">
select * from tb_student
</select>
<mapper namespace="multiTableQueryMapper.ClazzMapper">
<select id="selById" resultType="Clazz">
select * from tb_class where id=#{id}
</select>
</mapper>
一对多查询
关联查询。使用collection,javaType指定实际的类型而非像association指定泛型,使用ofType指定集合中的泛型。
<resultMap id="cao" type="Clazz" autoMapping="true">
<collection property="students" javaType="list"
ofType="Student" autoMapping="true">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
<select id="selAll" resultMap="cao">
select c.*, s.id sid, s.name sname, s.gender, s.birthday
from tb_student s
left join
tb_class c
on c.id=s.cid
</select>
N+1查询
<resultMap id="kao" type="Clazz">
<!--如果这一列作为参数继续传递, 会导致自动映射失败-->
<id property="id" column="id"/>
<collection property="students" javaType="list"
ofType="Student" column="id"
select="oneToMutil.UserMapper.selById"/>
</resultMap>
<select id="selAll2" resultMap="kao">
select * from tb_class
</select>
<select id="selById" resultType="Student">
select * from tb_student where cid=#{id}
</select>
关联查询与N+1对比
关联查询:
sql语句是关联语法, 需要使用join…on…
只需要执行1条SQL语句
默认情况下, 不会自动映射, 可以通过设置autoMapping=true开启自动映射
N+1查询:
SQL语句都是单表查询
需要执行N+1次查询才能得到结果
默认情况下, 就会自动映射.
自连接
N+1根据据领导编号查询树结构员工信息。员工号作为要查询的上级管理者编号,做n次查询。
<resultMap id="tree" type="Emp">
<id property="empno" column="empno"/>
<!--当继续查询的sql在当前命名空间下时, 命名空间可以省略-->
<collection property="emps" javaType="list"
ofType="Emp"
column="empno"
select="sel4Tree"/>
</resultMap>
<select id="sel4Tree" resultMap="tree">
select * from tb_emp
<where>
<choose>
<when test="mgr==null">
and mgr is null
</when>
<otherwise>
and mgr=#{mgr}
</otherwise>
</choose>
</where>
</select>
public List<Emp> sel4Tree(Integer mgr);
业务装配实现自连接
只需要查询一次,通过自定义代码实现业务装配。
public void test1() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
nPlusOne mapper = sqlSession.getMapper(nPlusOne.class);
List<Emp> emps = mapper.selAll();
List<Emp> tree = listToTree(emps);
for (Emp emp : tree) {
System.out.println(emp);
}
}
private List<Emp> listToTree(List<Emp> emps) {
List<Emp> empList = new ArrayList<>();
Map<Integer, Emp> empMap = new HashMap<>();
// 封装到map可以快速获取管理者,不用直接遍历,减少时间复杂度
for (Emp emp : emps) {
empMap.put(emp.getEmpno(), emp);
}
// 遍历员工并添加到管理者中
for (Emp emp : emps) {
if (emp.getMgr()==null){
empList.add(emp);
continue;
}
// 先通过员工信息中的管理者编号获取管理者,在添加管理者中
// 实体类中的list不new会报空指针异常
empMap.get(emp.getMgr()).getEmps().add(emp);
}
return empList;
}
<select id="selAll" resultType="Emp">
select * from tb_emp
</select>