听说微信搜《Java鱼仔》会让技术提高更快哦
在前面的学习中,我们差不多把Mybatis的基本增删改查、配置文件的配置都讲解了一遍,但是在实际的开发中我们编写的sql不会那么简单,今天就来模拟复杂环境的Sql查询
(一)resultMap结果映射
resultMap 元素是 MyBatis 中最重要最强大的元素,我们之前所写的sql语句,返回值都是简单的基本数据类型或者某一个实体类,比如下面这段sql返回的就是最简单的User类型。
<selectid="getUserById"resultType="com.javayz.pojo.User"parameterType="int"> select * from user where id=#{id}; </select>
现在思考一下下面这种情况,如果实体类中定义的某一个字段和数据库中的字段不一致怎么办
publicclassUser { privateintid; privateStringlastname; //.....}
比如我定义了一个User类,包含id和lastname两个属性,而数据库中这两个字段的名字为id和name。此时再执行查询时结果如下:lastname这个字段直接为null
这时候我们就可以使用resultMap来解决这个问题,resultMap可以讲数据库中的字段映射到实体类上。column代表数据库中的字段名,properties代表实体类中的字段名,通过映射之后Mybatis就可以找到对应的字段。
<resultMapid="UserMap"type="User"><!--column代表数据库中的字段名,properties代表实体类中的字段名--><resultcolumn="id"property="id"/><resultcolumn="name"property="lastname"/></resultMap><selectid="getUserById"resultMap="UserMap"parameterType="int"> select id,name from user where id=#{id}; </select>
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
(二)复杂环境下的resultMap使用
在实际的应用场景中,我们会遇到很多比较复杂的业务逻辑,这里resultMap的优势才会被放大。因此我们通过一个案例来实现复杂环境的查询。
//创建教室表 createtable classroom ( id intnotnull AUTO_INCREMENT, classname VARCHAR(40)notnull, PRIMARY KEY (id))//创建学生表 createtable student ( id intnotnull AUTO_INCREMENT, name VARCHAR(40)notnull, classid intnotnull, PRIMARY KEY (id), FOREIGN key (classid) REFERENCES classroom(id))//创建一些数据 insertinto classroom VALUES(1,'101班')insertinto classroom VALUES(2,'102班')insertinto student VALUES(1,'Amy',1);insertinto student VALUES(2,'Bob',1);insertinto student VALUES(3,'javayz',1);
在Java的实体类代码中分别建立Student和ClassRoom的类
publicclassStudent { privateintid; privateStringname; privateClassRoomclassRoom; //省略构造方法、get、set、toString//务必给出无参构造方法}
ClassRoom 类
publicclassClassRoom { privateintid; privateStringclassname; //省略构造方法、get、set、toString//务必给出无参构造方法}
2.1 多对一查询(association)
多对一查询在上面这个案例中表现为多个学生在一个教实中,因此这里的Student类中的第三个变量我们设置为ClassRoom类。
现在我们通过两种方式进行多对一查询:
在mapper路径下创建StudentMapper和studentMapper.xml
其中StudentMapper接口定义一个查询方法:
publicinterfaceStudentMapper { List<Student>selectAllStudent(); }
查询方法一:通过类似嵌套子查询的方式查询:
<selectid="selectAllStudent"resultMap="StudentAndClassRoom"> select * from student </select><resultMapid="StudentAndClassRoom"type="Student"><resultproperty="id"column="id"/><resultproperty="name"column="name"/><!--对于复杂类型,对象使用association处理,集合使用collection--><associationproperty="classRoom"column="classid"javaType="ClassRoom"select="getClassRoom"></association></resultMap><selectid="getClassRoom"resultType="ClassRoom"> select * from classroom where id = #{classid} </select>
首先通过 select * from student 语句查询到所有数据,返回类型通过resultMap进行映射,id和name字段属于基本数据类型字段不需要改动,由于classRoom是一个对象方法,因此需要用association 进行处理,实体类中的属性名为classRoom,与Student表进行连接的字段是classid,Java类是ClassRoom,通过这几个属性,继续使用select查询。
所以上面一段代码总结下来就是两句话:
1、查询student表。
2、通过查询出来的classid值查询classRoom表。
通过测试类测试结果:
publicvoidtestSelect(){ SqlSessionsqlSession=MybatisUtils.getSqlSession(); StudentMappermapper=sqlSession.getMapper(StudentMapper.class); List<Student>students=mapper.selectAllStudent(); System.out.println(students); }
结果如下:
[Student{id=1, name='Amy', classRoom=ClassRoom{id=1, classname='101班'}}, Student{id=2, name='Bob', classRoom=ClassRoom{id=1, classname='101班'}}, Student{id=3, name='javayz', classRoom=ClassRoom{id=1, classname='101班'}}]
查询方法二:结果嵌套查询
<selectid="selectAllStudent"resultMap="StudentAndClassRoom"> select s.id sid,s.name sname,c.id cid,c.classname cname from student s,classroom c where s.classid=c.id </select><resultMapid="StudentAndClassRoom"type="Student"><resultproperty="id"column="sid"/><resultproperty="name"column="sname"/><associationproperty="classRoom"javaType="ClassRoom"><resultproperty="id"column="cid"/><resultproperty="classname"column="cname"/></association></resultMap>
我个人比较喜欢结果嵌套查询,所有的sql语句写在select语句中,根据结果要返回的值在resultMap中填写对应的数据。
结果与第一种方式相同。
2.2 一对多查询(collection)
修改一下之前的两个实体类,来实现一对多的查询,每个教室里有多个学生:
publicclassStudent { privateintid; privateStringname; privateintclassId; //省略get、set、toString方法//务必给出无参构造方法}
publicclassClassRoom { privateintid; privateStringclassname; privateList<Student>students; //省略get、set、toString方法//务必给出无参构造方法}
接着编写Mapper接口和对应的Mapper.xml
publicinterfaceClassRoomMapper { List<ClassRoom>getClassRoomByid( ("id") intid); }
在这里我只介绍结果嵌套查询的方式。先通过sql查询出字段信息,再通过resultMap进行展示。
<mappernamespace="com.javayz.mapper.ClassRoomMapper"><selectid="getClassRoomByid"resultMap="ClassRoomAndStudent"parameterType="int"> select c.id cid,c.classname cname,s.id sid,s.name sname,s.classid classid from student s,classroom c where s.classid=c.id and c.id=#{id} </select><resultMapid="ClassRoomAndStudent"type="ClassRoom"><resultproperty="id"column="cid"/><resultproperty="classname"column="cname"/><!--对于集合属性,需要使用collection来实现--><collectionproperty="students"ofType="Student"><resultproperty="id"column="sid"/><resultproperty="name"column="sname"/><resultproperty="classId"column="classid"/></collection></resultMap></mapper>
需要注意几个点,对于集合属性,我们需要使用collection。这里的集合属性中用到了ofType字段,该字段表示集合泛型中所使用的泛型对象类型。
(三)总结
到这里为止,Mybatis的各种查询以及配置用法都讲完了,对于复杂类型,注意以下几点:
1、对象的关联使用association,集合的关联使用collection。
2、官网的resultMap中还有两个参数没有使用:
首先讲一下constructor ,constructor主要是用来配置构造方法,默认情况下mybatis会调用实体类的无参构造方法创建一个实体类,如果在实体类中配置了有参构造方法而没有配无参构造,就会报错,此时可用constructor 注入构造方法。
discriminator 鉴别器,可以通过case的值来决定使用哪个resultMap。