需求:查询所有的学生信息,以及学生对应的老师的信息
需求分析
所以我们需要查询的字段就是学生ID,学生name和老师name,要查询两张表上的数据,且这两张表有主外键关系,所以我们应该使用联表查询
SQL:SELECT s.id,s.name,t.name FROM student s,teacher t where s.tid = t.id;
代码实现
在13搭建环境进行测试的时候,我们发现了一个BUG
即查询出来的数据封装为student对象之后,他们的teacher属性都是NULL,原因很简单,因为我们只查询了一张student表,这张表没有teacher的实体类对应的字段,所以teacher的set方法不会被调用
而如果我们使用上面的联表查询,我们很快就能使用SQL语句查询出来满意的结果,但是在Java程序中怎么实现呢?怎么将查到的结果封装为一个teacher对象呢?
注意:这里我们不能直接将上面联表查询的SQL语句放在mapper接口的注释中进行查询,因为这样查询出来的student对象的teacher属性仍然为NULL
原因:还是因为我们联表查询出来的只是老师的一个属性name,而不是teacher的对象,所以student对象的teacher属性的set并不会被调用,所以student对象的teacher属性为NULL
解决:总的来说,造成上面查询结果为NULL的原因就是数据库中的字段和POJO字段不匹配的结果,在前面的第8篇博客中我们说了解决的办法,就是使用resultMap来解决,即通过resultMap来映射名称不相同的字段和属性
1.按照查询嵌套查询,即使用子查询
按照查询嵌套处理的意思就是:查询里面嵌套查询 —— 对应MYSQL子查询
怎么将这两个结果结合起来?使用resultMap,由于我们是要在查询学生的时候获取老师的信息,所以resultMap应该在查询学生信息的select节点上使用
注意:上图中的javaType是在指定column中的"tid"是一个teacher对象,又因为我们指定的子查询得到的结果是teacher表中一行记录封装成的teacher实体类对象,所以在查询每个学生信息的时候首先会查询到他的老师这个对象,并将对象的引用指向tid,然后我们再通过resultMap将tid赋值给student对象的teacher属性,这样就完成了一个学生对象的teacher属性的赋值
测试
2.按照结果嵌套结果,即使用联表查询
结果嵌套结果的意思就是:结果里面嵌套结果 —— 对应MYSQL联表查询
查询嵌套查询我们好理解,就是外面的查询条件是里面查询语句的结果;而结果里面嵌套结果的意思其实差不多,就是外层要返回的查询结果中某些属性的值是其内部嵌套返回的查询结果的值
- 接口
package com.thhh.dao; import com.thhh.pojo.Student; import java.util.List; public interface StudentMapper { //1、按照查询嵌套处理 List<Student> selectStudentList(); //2、按照结果嵌套处理 List<Student> selectStudentList2(); }
- mapper.xml
<select id="selectStudentList2" resultMap="StudentTeacher2"> SELECT s.id sid,s.name sname,t.name tname FROM student s,teacher t where s.tid = t.id </select> <resultMap id="StudentTeacher2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
- 测试
@Test public void testSelectStudentList2(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> studentList = mapper.selectStudentList2(); for (Student student : studentList) { System.out.println(student); } sqlSession.close(); }
通过上面的例子我们可以看出,MYSQL中多对一的查询方式就两种
- 子查询,查询嵌套查询
- 联表查询,结果嵌套结果
小结
通过上面的两个例子我们再来理解一下mapper.xml文件是怎么将数据库中的数据查询出来,并且映射为一个实体类返回的
①简单查询
②查询嵌套查询
③结果嵌套结果
通过上面3个例子我们可以看出mapper.xml文件或者说mybatis进行POJO和数据表中一条记录之间数据映射的时候,首先是按照SQL语句获取数据库的数据,然后按照指定的映射/赋值规则将数据库中的字段的值赋值给一个POJO的对应属性,最后再将POJO实例返回,这个过程代替类原来JDBC中while(next())的循环体中new对象和调用对象的set()为对象对应属性赋值最后返回对象的操作,mybatis将JDBC的这个操作通过mapper.xml文件实现
注意:上面的3个举例中我并没有说带参数的情况,比如例2,它的子查询就使用了参数id,那么id是怎么传进去的?
参数的传递也是依赖于我们的mybatis的自动匹配,我们的复杂属性映射中本来从数据表中查询出来的值是一个外键的值,这个值是确确实实查询出来了,然后执行到了javatype,表示查询返回的属性不是外键这个值,而是一个对象;然后继续执行到select属性,就去调用了子查询
子查询中使用到了参数,那么mybatis怎么自动匹配呢?它不会管你在#{ }到底填了什么变量名称,它只会去调用子查询的地方获取外键的值,因为要使用子查询,必定要涉及两张表,涉及了两张表还敢使用条件查询,那这两张表必然有外键关联,所以它直接去调用处找外键,你在#{ }写什么没关系,但是为了标准,我们一般将参数名称与外键名称保持一致
既然你在#{ }写什么没关系,那么为什么还要使用它呢?当然是起到占位的作用,#{ }的作用相当于SQL中的?,只有SQL中有?mybatis才回去自动找外键的值替代?