关联映射的本质就是连表查询,用于多对一和一对多的表结构。
首先我们来新建两张表,student表和teacher表,在这里我们规定一个老师可以对应多个学生,而一个学生只能对应一个老师。
多对一查询
我们创建对应的实体类
连表查询
<select id="selectStudentTeacher" resultMap="StudentTeacher">
SELECT student.*,teacher.Tname from
student left join teacher on teacher.id = student.t_id
</select>
<!-- resultMap:返回映射,要保证数据库字段和实体类字段的一致 -->
<resultMap id="StudentTeacher" type="com.qcby.entity.Student">
<!-- property:实体类当中的字段; column:数据库当中的字段 -->
<result property="id" column="id"/>
<result property="sName" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<!-- association:复杂的属性要特殊处理 ;javaType:复杂属性的类型 -->
<association property="teacher" javaType="com.qcby.entity.Teacher">
<result property="id" column="id"/>
<result property="tName" column="Tname"/>
</association>
</resultMap>
分步查询
<!-- 分步查询 -->
<!-- select * from student:首先查询一个表的全部信息-->
<!-- select * from teacher where id = #{t_id} :然后根据两个表的关联字段查询另一个表的信息 -->
<select id="StudentTeacher2" resultMap="StudentTeacher2">
select * from student
</select>
<resultMap id="StudentTeacher2" type="com.qcby.entity.Student">
<result property="id" column="id"/>
<result property="sName" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
<!-- column="t_id" :两个表的关联字段 -->
<!-- select="teacher1" : 调用teacher1方法-->
<association property="teacher" javaType="com.qcby.entity.Teacher" column="t_id" select="teacher1"/>
</resultMap>
<select id="teacher1" resultType="com.qcby.entity.Teacher">
select * from teacher where id = #{t_id}
</select>
可以看到,虽然查询方式不同,但是查询结果相同。
一对多查询
我们需要修改两个实体类信息,因为是一对多,所以应该在老师类里添加学生集合
连表查询
<select id="getTeacherStudent" resultMap="teacherStudent">
SELECT student.*,teacher.Tname from
student left join teacher on teacher.id = student.t_id
</select>
<resultMap id="teacherStudent" type="com.qcby.entity.Teacher2">
<result property="id" column="id"/>
<result property="tName" column="Tname"/>
<!-- 复杂的属性我们需要单独去处理 collection:处理集合 ;association:处理对象-->
<!-- ofType:在集合中的泛型类型 -->
<collection property="student2s" ofType="com.qcby.entity.Student2">
<result property="id" column="id"/>
<result property="sName" column="Sname"/>
<result property="sex" column="sex"/>
<result property="age" column="age"/>
<result property="t_id" column="t_id"/>
</collection>
</resultMap>
分步查询
<!--分步查询 -->
<!--select * from teacher -->
<!--select * from student where t_id = #{id} -->
<select id="getTeacherStudent2" resultMap="teacherStudent2">
select * from teacher
</select>
<resultMap id="teacherStudent2" type="com.qcby.entity.Teacher2">
<result property="id" column="id"/>
<result property="tName" column="Tname"/>
<collection property="student2s" ofType="com.qcby.entity.Student2" column="id" select="getStudent"/>
</resultMap>
<select id="getStudent" resultType="com.qcby.entity.Student2">
select * from student where t_id = #{id}
</select>
Teacher2{id=1, tName='张老师', student2s=[Student2{id=1, sName='张三', sex='男', age=18, t_id=1}, Student2{id=2, sName='李四', sex='女', age=18, t_id=1}, Student2{id=3, sName='王五', sex='男', age=18, t_id=1}, Student2{id=4, sName='小白', sex='女', age=18, t_id=1}, Student2{id=5, sName='小黑', sex='男', age=18, t_id=1}]}
Teacher2{id=2, tName='李老师', student2s=[Student2{id=6, sName='小红', sex='女', age=20, t_id=2}, Student2{id=7, sName='小李', sex='男', age=20, t_id=2}, Student2{id=8, sName='小张', sex='女', age=20, t_id=2}, Student2{id=9, sName='小赵', sex='男', age=20, t_id=2}, Student2{id=10, sName='小王', sex='女', age=20, t_id=2}]}
可以看到查询结果依然相同。
延迟加载策略
首先我们在主配置文件中设置延迟加载
<setting name="lazyLoadingEnabled" value="true"/> //延迟加载的全域开关,开启时所有关联对象都会延迟加载。
<setting name="aggressiveLazyLoading" value="false"/> //当它开启时,任何方式的调用都会加载该对象的所有属性,否则就是按需加载。
我们上述所写分步查询是有缺陷的,为了初步理解分步查询,我们在查询老师(学生)时,将方法写到了学生(老师)的目标xml文件中。这么写在开发中是不合理的。
我们将分步查询的第二步写回到对应的文件中,这样各自查询时皆可调用该方法。
我们以一对多中查询老师主类为例,将分步查询第二步放回到StudentDao.xml文件中。注意放回时我们需要给方法加入参,这样StudentTset方法可以自己调用getStudent方法。
<select id="getStudent" resultType="com.qcby.entity.Student2" parameterType="java.lang.Integer">
select * from student where t_id = #{id}
</select>
getStudent方法写回之后,collection处理学生集合时调用getStudent方法也需要修改:
<collection property="student2s" ofType="com.qcby.entity.Student2"
column="id" select="com.qcby.dao.StudentDao.getStudent"
/>
我们来测试一下
首先查询所有的teacher信息,这里包含student集合,所以该测试方法会进行两步查询。
public void getTeacherStudent2(){
List<Teacher2> teachers = mapper.getTeacherStudent2();
for (Teacher2 teacher : teachers) {
System.out.println(teacher);
}
}
接下来我们仅查询老师的名字,这里并不涉及到学生信息,请注意查询结果。
public void getTeacherStudent2(){
List<Teacher2> teachers = mapper.getTeacherStudent2();
for (Teacher2 teacher : teachers) {
System.out.println(teacher.gettName());
}
}
我们对collection添加fetchType = "eager" 再来查询老师的名字,按照上述结果,这里不涉及学生信息,本应只执行一条查询语句。请注意查询结果。
<collection property="student2s" ofType="com.qcby.entity.Student2"
column="id" select="com.qcby.dao.StudentDao.getStudent"
fetchType="eager"
/>
public void getTeacherStudent2(){
List<Teacher2> teachers = mapper.getTeacherStudent2();
for (Teacher2 teacher : teachers) {
System.out.println(teacher.gettName());
}
}
我们可以看到,在打印出我们想要的结果之前,所有的查询语句都执行了,这是因为我们设置了fetchType属性,默认为fetchType = "lazy" 即延迟加载生效;当我们将其设定为eager时,延迟加载便失效了。