resultMap是MyBatis里面最复杂的元素,它的作用是:
定义映射规则,级联的更新,定制类型转化器等等。 resultMap定义的主要是一个结果集的映射关系。
1.resultMap元素的构成
resultMap元素里面还有以下元素,如下:
<resultMap>
<constructor>
<idArg/>
<arg/>
</constructor>
<id />
<result />
<association/>
<collection/>
<discriminator>
<case/>
</discriminator>
</resultMap>
我们先简单的了解下其中的:
constructor元素用于配置构造方法。一个POJO可能不存在没有参数的构造方法,这个时候我们就可以使用constructor进行配置。
假设角色类RoleBean不存在没有参数的构造方法,它的构造方法声明为public RoleBean(Integer id,String roleName),那么我们需要配置这个结果集,如下:
<resultMap ......>
<constructor>
<idArg column="id" javaType="int" />
<arg column="role_name" javaType="string" />
</constructor>
......
</resultMap>
如此MyBatis就知道要用这个构造方法来构造POJO了。
id元素是表示哪个列是主键,允许多个主键,多个主键则称为联合主键。result是配置POJO到SQL列名的映射关系。这里的id和result两个元素都有下面表的属性:
2.使用map存储结果集
一般而言,任何的select语句都可以使用map存储结果集。
例如:
<select id="findColorByNote" parameterType="string" resultType="map">
select id ,color,note from t_color where note like concat('%',#{note},'%')
select>
使用map原则上是可以匹配所有结果集的,但是使用map的方式我们的可读性就会下降,更多的时候使用POJO的方式。
3.使用POJO存储结果集
POJO的方式很方便,一方面我们可以使用自动映射,我们可以使用select语句的属性resultMap配置映射集合,只是使用前需要配置类似的resultMap。
例如:
<resultMap id="roleResultMap" type="com.learn.chapter4.pojo.Role">
<id property="id" column="id" />
<result property="roleName" column="role_name" />
<result property="note" column="note" />
resultMap>
然后我们就可以使用它了:
<select parameterType="long" id="getRole" resultMap="roleResultMap">
select id,role_name,note from t_role where id =#{id}
select>
我们可以发现SQL语句的列名和roleResultMap的column是一一对应的。
4.级联
在数据库中包含着一对多,一对一的关系,比方说一个角色可以分配给多个用户,也可以只分配给一个用户。有时候我们希望角色信息和用户信息一起显示出来,这是很常见的场景,所以会经常遇到下面的SQL:
select r.*,u.* from t_role r inner join t_user_role ur
on r.id = ur.id inner join t_user u on ur.user_id = u.id
where r.id = #{id}
上面的查询是把角色和用户的信息都查询出来,我们希望的是在角色的信息中多一个属性,即List userList 这样取出Role的同时也可以访问到它下面的用户了,
这样的情况叫做级联,在级联中存在三种对应关系:一对多,一对一,多对多。在实际情况中,多对多的关系应用不多,因为比较复杂,会增加理解和关联的复杂度,在这里我们推荐的是用一对多的关系把它分解为双向关系,以降低关系的复杂度,简化程序。
在MyBatis中级联分为三种:association,collection和discriminator ,下面分别介绍下:
1.association ,代表一对一关系
实际操作中,首先我们需要确定对象的关系,以学习信息级联为例,在学校里学生表和学生证表是一对一的关系,因此我们建立一个StudentBean和StudentSelfcardBean的POJO对象。那么在StudentBean中应该有一个类型为StudentSelfcardBean的属性,这样便形成了级联。
下面是例子代码:
这时候我们需要建立Student的映射器和StudentSelfcard的映射器。
StudentSelfcardMapper映射器
<mapper namespace="com.learn.chapter4.mapper.StudentSelfcardMapper">
<resultMap id="studentSelfcardMap" type="com.learn.chapter4.po.StudentSelfcardBean">
<id property="id" column="id" />
<result property="studentId" column="student_id" />
<result property="native_" column="native" />
<result property="issueDate" column="issue_date" />
<result property="endDate" column="end_date" />
<result property="note" column="note" />
resultMap>
<select id="findStudentSelfcardByStudentId" parameterType="int" resultMap="studentSelfcardMap">
select id , student_id,native,issue_date,end_date,note
from t_student_selfcard where student_id =#{studentId}
select>
mapper>
有了上面的代码,我们便可以进行级联操作了:
StudentMapper映射器
<mapper namespace="com.learn.chapter4.mapper.StudentMapper">
<resultMap id="studentMap" type="com.learn.chapter4.po.StudentBean">
<id property="id" column="id" />
<result property="cnname" column="cnname" />
<result property="sex" column="sex" jdbcType="INTEGER"
javaType="com.learn.enums.SexEnum"
typeHandler="com.learn.typehandler.SexTypeHandler"/>
<result property="note" column="note" />
<association property="studentSelfcard" column="id"
select="com.learn.chapter4.mapper.StudentSelfcardMapper.findStudentSelfcardByStudentId"/>
resultMap>
<select id="getStudent" parameterType="int" resultMap="studentMap">
select id,cnname,sex,note from t_student where id =#{id}
select>
mapper>
上面的association其中select元素由指定的SQL去查询,而column则是指定传递给select语句的参数,这里是学生对象的id。当取出Student的时候,MyBatis就会知道用下面的SQL取出我们需要的级联信息。
2.collection,代表一对多关系
一个学生可能有多门课程,在学生确定的前提下每一门课程都会有自己的分数,所以每一个学生的课程成绩只能对应一门课程。
所以这里有两个级联:
一个是学生和课程成绩的级联,这里是一对多的关系。
一个是课程成绩和课程的级联,这里是一对一的关系。
这个时候我们需要建立一个LectureBean来记录课程,而学生课程表则是建立一个StudentLectureBean,里面有一个类型为LectureBean属性的lecture,用来记录学生成绩。
public class LectureBean{
private Integer id;
private String lectureName;
private String note;
}
public class StudentLectureBean{
private int id;
private Integer studentId;
private LectureBean lecture;
private BigDecimal grade;
private String note;
}
然后编写映射器代码:
StudentMapper映射器
<mapper namespace="com.learn.chapter4.mapper.StudentMapper">
<resultMap id="studentMap" type="com.learn.chapter4.po.StudentBean">
<id property="id" column="id" />
<result property="cnname" column="cnname" />
<result property="sex" column="sex" jdbcType="INTEGER"
javaType="com.learn.enums.SexEnum"
typeHandler="com.learn.typehandler.SexTypeHandler"/>
<result property="note" column="note" />
<association property="studentSelfcard" column="id"
select="com.learn.chapter4.mapper.StudentSelfcardMapper.findStudentSelfcardByStudentId"/>
<collection property="studentLectureList" column="id"
select="com.learn.chapter.mapper.StudentLectureMapper.findStudentLectureByStuId"
/>
resultMap>
<select id="getStudent" parameterType="int" resultMap="studentMap">
select id,cnname,sex,note from t_student where id =#{id}
select>
mapper>
StudentLectureMapper映射器
<mapper namespace="com.learn.chapter4.mapper.StudentLectureMapper">
<resultMap id="studentLectureMap" type="com.learn.chapter4.po.StudentLectureBean">
<id property="id" column="id" />
<result property="studentId" column="student_id" />
<result property="grade" column="grade" />
<result property="note" column="note" />
<association property="lecture" column="lecture_id"
select="com.learn.chapter4.mapper.LectureMapper.getLectrue"
/>
resultMap>
<select id="findStudentLectureByStuId" parameterType="int" resultMap="studentSelfcardMap">
select id , student_id,lecture_id,grade,note
from t_student_lecture where student_id =#{id}
select>
mapper>
LectureMapper映射器
<mapper namespace="com.learn.chapter4.mapper.LectureMapper">
<select id="getLecture" parameterType="int"
resultType="com.learn.chapter4.po.LectureBean">
select id ,lecture_name as lectureName,note from t_lecture where id=#{id}
select>
mapper>
3.discriminator, 它是鉴别器,
可以根据实际选择采用哪个类作为实例,允许你根据特定的条件去关联不同的结果集。
鉴别器级联是在特定的条件下去使用不同的POJO。比如本例中要了解学生的健康情况,如果是男生我们不能去了解他的女性生理指标,我们应该对症下药。这个时候我们就需要鉴别器了。
我们可以根据学生信息中的性别属性进行判断去关联男女性的健康指标,然后进行关联即可,在MyBatis中我们采用的是discriminator,由它来处理需要鉴别的场景,它相当于java的switch语句。
下面我们通过例子来了解:
还是以男女生为例,首先,我们需要新建两个健康状况的POJO:
public class MaleStudentBean extends StudentBean{
private List<StudentHealthMaleBean> studentHealMaleList=null;
}
public class FemaleStudentBean extends StudentBean{
private List<StudentHealthFemaleBean> studentHealthFemaleList=null;
}
然后鉴别,因此我们找学生信息就要根据StudentBean的属性sex来确定是使用男学生还是女学生的对象了。下面是使用discriminator级联完成此功能的:
<mapper namespace="com.learn.chapter4.mapper.StudentMapper">
<resultMap id="studentMap" type="com.learn.chapter4.po.StudentBean">
<id property="id" column="id" />
<result property="cnname" column="cnname" />
<result property="sex" column="sex" jdbcType="INTEGER"
javaType="com.learn.enums.SexEnum"
typeHandler="com.learn.typehandler.SexTypeHandler"/>
<result property="note" column="note" />
<discriminator javaType="int" column="sex">
<case value="1" resultMap="maleStudentMap" />
<case value="2" resultMap="femaleStudentMap" />
discriminator>
resultMap>
<rusultMap id="maleStudentMap"
type="com.learn.chapter4.po.MaleStudentBean" extends="studentMap"
>
<rusultMap id="femaleStudentMap"
type="com.learn.chapter4.po.FemaleStudentBean" extends="studentMap"
>
mapper>
我们在discriminator里配置了case,这样我们就可以在case里面引入resultMap,当sex=1时,引入的时maleStudentMap,否则引入另一个。
正如类的继承关系一样,resultMap也是可以继承的,再加入自己的属性。