解决数据库字段名与属性名不一致的映射问题、resultMap详解(多对一关系映射问题)、一对多关系映射问题
解决数据库字段名与属性名不一致的映射问题
a>为字段起别名来保持和属性名的一致,在不一致的字段后加上 属性名:
select eid,emp_name eName,gender,age,dept from employeeinfo
b>通过全局配置文件MybatisConfig.xml,配置驼峰映射法,这样字段名中的_下划线会自动映射为符合驼峰命名法的属性名,如emp_name--->empName:
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
c>通过resultMap标签设置自定义映射,<select>标签中resultMap的值需要与<resultMap>标签中id的值保持一致,<resultMap>中type用来设置映射关系的实体类类型

<resultMap>标签中,子标签<id>用来设置主键映射关系,子标签<result>用来设置其他字段与属性名的映射关系,子标签中的property值为属性名,colum值为字段名
ResulMap详解:
有如下学生表和老师表,学生与老师是多对一的关系,,tid与teacher表的id关联


对应的实体类信息:


我们想查询一名同学和这位同学对应的老师信息,数据库执行结果如下:

那在mybatis中如果我们用普通的Student类型来接收会有什么结果呢:
public Student selectSandTById(@Param("sid") Integer id);
<select id="selectSandTById" resultType="pojo.Student">
select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
</select>
查询结果:
Student(id=1, name=学生1, age=20, teacher=null)
可以看到,学生的信息可以正常查询出来,但是老师的信息为null,说明mybatis无法将后面查询的老师数据映射到Teacher类型中,但我们又不能在resultType中声明两个实体类,所以,如果我们想同时查询学生和老师的信息,就需要用到resultMap标签来自定义映射关系,有如下几种方式:
a>通过级联属性赋值解决多对一的映射关系
public Student selectSandTById(@Param("sid") Integer id);
<select id="selectSandTById" resultMap="StudentAndTeacher">
select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
</select>
<resultMap id="StudentAndTeacher" type="pojo.Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<result property="teacher.id" column="id"></result>
<result property="teacher.name" column="name"></result>
</resultMap>
结果:Student(id=1, name=学生1, age=20, teacher=Teacher(id=1, name=学生1))
这里Teacher的信息被Student的信息覆盖的原因时:teacher表与student的id,name字段重名
使用resultMap标签的注意点:
1.<select>标签中resultMap的值需要与<resultMap>标签中id的值保持一致,不然mybatis怎么知道它要去找哪一个resultMap
2.<resultMap>中type用来设置映射关系的实体类类型,我们需要Student类型数据,其中包含了Teacher类型数据,所以type中指定的是Student类型
3. <resultMap>标签中,子标签<id>用来设置主键映射关系(也就是student表的主键),子标签<result>用来设置其他字段与属性名的映射关系,子标签中的property值为属性名,colum值为字段名
4.级联属性的使用方法为:对象名.属性,例如我们要把teacher表中的数据映射到teacher对象中,所以property是teacher.id,teacher.name
虽然结果是成功的查询出来了,但是我发现了一个问题,也是我在设计表的时候的一个疏忽,student表和teacher表中他们的id和name字段名都一样:

所以在指定column值的时候,就出现了值一致情况,然后我尝试了使用表名.字段的方式来查询:
<select id="selectSandTById" resultMap="StudentAndTeacher">
select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
</select>
<resultMap id="StudentAndTeacher" type="pojo.Student">
<id property="id" column="student.id"></id>
<result property="name" column="student.name"></result>
<result property="age" column="age"></result>
<result property="teacher.id" column="teacher.id"></result>
<result property="teacher.name" column="teacher.name"></result>
</resultMap>
结果:Student(id=1, name=学生1, age=20, teacher=null)
个人猜测是如果出现两表字段名重复的情况,mybatis会按字段出现先后顺序赋值?
后续:我太粗心了,一开始没发现Teacher与Student查出来的id和name居然相同,然后再去网上查了Mybatis多表联查不同表字段名相同的解决方法,其实就是起别名,下面附上正确代码:
<select id="selectSandTById" resultMap="StudentAndTeacher">
select student.id id1,student.name name1,student.age,teacher.id id2,teacher.name name2
from student
left join teacher
on student.tid = teacher.id
where student.id = #{sid}
</select>
<resultMap id="StudentAndTeacher" type="pojo.Student">
<id property="id" column="id1"></id>
<result property="name" column="name1"></result>
<result property="age" column="age"></result>
<association property="teacher" javaType="pojo.Teacher">
<id property="id" column="id2"></id>
<result property="name" column="name2"></result>
</association>
</resultMap>
查询结果正确:Student(id=1, name=学生1, age=20, teacher=Teacher(id=1, name=老师1, studentList=null))
b>通过association标签解决
association标签是专门处理多对一关系的标签,和resultMap标签注意点一样,id设置主键,result设置其他值
public Student selectSandTById(@Param("sid") Integer id);
<select id="selectSandTById" resultMap="StudentAndTeacher">
select * from student left join teacher on student.tid = teacher.id where student.id = #{sid}
</select>
<resultMap id="StudentAndTeacher" type="pojo.Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<association property="teacher" javaType="pojo.Teacher">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
</association>
</resultMap>
c>通过分布查询解决(常用)
我将这种方式理解为SQL语句的子查询:先执行一个SQL语句,该语句的查询结果作另一个SQL语句查询条件。
分布查询分为两个步骤:第一步是先查询出student的信息,第二部是通过student信息中的tid去查询teacher的信息,他们之间关系连接依旧是使用association标签
/**
* 分布查询第一步,根据id查询出student的信息
* @param id
* @return
*/
public Student selectStudentAndTeacherStepOne(@Param("sid") Integer id);
/**
* 分布查询第二步,根据student信息中的tid去查找teacher信息
* @param id
* @return
*/
public Teacher selectStudentAndTeacherStepTwo(@Param("tid") Integer id);
//第一步查询
<select id="selectStudentAndTeacherStepOne" resultMap="StepOne">
select * from student where id = #{sid}
</select>
//分布查询的桥梁
<resultMap id="StepOne" type="pojo.Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="age" column="age"></result>
<association property="teacher"
select="mapper.StudentMapper.selectStudentAndTeacherStepTwo"
column="tid">
</association>
</resultMap>
//第二步查询
<select id="selectStudentAndTeacherStepTwo" resultType="pojo.Teacher">
select * from teacher where id = #{tid}
</select>
@Test
public void selectSandTByTwoStep(){
SqlSession session = this.getConn();
StudentMapper mapper = session.getMapper(StudentMapper.class);
System.out.println(mapper.selectStudentAndTeacherStepOne(2));
}
association标签作为分布查询连接的桥梁,它的设置最为重要,注意点如下:
1.property值为需要映射表信息的对象名。我们第二布查询的目的是查询teacher表对应信息赋值给Student的teacher对象,所以值为teacher。
2.select:设置分布查询第二步的SQL的唯一标识,值为namespace.SQLId,也就是mapper接口全类名.方法名。设置这个的目的是告诉mybatis,分布查询的第二步要去执行哪条SQL语句,所以是第二次查询SQL的唯一标识id。
3.column:设置分布查询第二部的查询条件。我们需要根据student信息中的tid,作为第二步查询的入参,也就是说第二步查询的条件是id=#{tid},所以该值为tid。
分步查询的优点:可以实现延迟加载(懒加载)。
但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。
如果配置了全局的按需加载,又有某处特殊的需求需要延迟加载,此时可通过association和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加载)|eager(立即加载)"。

一对多关系映射问题
前面是学生对于老师是多对一关系,如果现在改成老师对应学生就变成了一对多关系。我们只需要明白映射关系中,对一用对象接收,对多用集合接收,所以在Teacher类中我们用List集合来存放Student:

处理一对多关系我们有两种方式:
a>collection标签
collection顾名思义就是集合了,所以用collection标签来处理一对多中集合的映射再合适不过了,需要注意的就是ofType:设置collection标签所处理的集合属性中存储数据的类型
<select id="selectTeacherAndStudentsById" resultMap="TeacherAndStudentResultMap">
SELECT t.id id1,t.name name1,s.id id2,s.name name2,s.age
FROM teacher t
LEFT JOIN student s
ON t.id = s.`tid`
WHERE t.`id` = #{tid};
</select>
<resultMap id="TeacherAndStudentResultMap" type="pojo.Teacher">
<id column="id1" property="id"></id>
<result column="name1" property="name"></result>
<collection property="studentList" ofType="pojo.Student">
<id column="id2" property="id"></id>
<result column="name2" property="name"></result>
<result column="age" property="age"></result>
</collection>
</resultMap>
结果:Teacher(id=1, name=老师1, studentList=[Student(id=1, name=学生1, age=20, teacher=null), Student(id=3, name=学生3, age=20, teacher=null)])
b>分布查询
/**
* 分布查询第一步,先查老师部分的信息
* @param tid
* @return
*/
public Teacher selectTeacherAndStudentsStepOne(@Param("tid") Integer tid);
/**
* 分布查询第二部,根据老师id查询学生list信息
* @param id
* @return
*/
public List<Student> selectTeacherAndStudentsStepTwo(@Param("id") Integer id);
<select id="selectTeacherAndStudentsStepOne" resultMap="TeacherAndStudentsResultMapByStep">
select * from teacher where id = #{tid}
</select>
<resultMap id="TeacherAndStudentsResultMapByStep" type="pojo.Teacher">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<collection property="studentList"
select="mapper.TeacherMapper.selectTeacherAndStudentsStepTwo"
column="id">
</collection>
</resultMap>
<select id="selectTeacherAndStudentsStepTwo" resultType="pojo.Student">
select * from student where tid = #{id}
</select>
结果:Teacher(id=1, name=老师1, studentList=[Student(id=1, name=学生1, age=20, teacher=null), Student(id=3, name=学生3, age=20, teacher=null)])
这次分布查询的桥梁时collection标签,其规则和association标签一样
浙公网安备 33010602011771号