MyBatis系列02——ResultMap、多对一、一对多

ResultMap

数据库的字段名Java中实体类的属性名不一致时,比如:
在这里插入图片描述
这个时候Mybatis自动映射就会失败,比如:
在这里插入图片描述
结果为:
在这里插入图片描述
这是因为,

select * from user where id = #{id}
等价于
select id,name,pwd from user where id = #{id}

mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的get/set方法 , 也就是说,查询出一行记录后,Mybatis会new一个User对象,然后调用setId()、setName()和setPwd()方法为user对象赋值,然后返回这个对象。但是,由于实体类User中并没有getPwd()方法 , 所以password返回null 。

解决方法:

  • 为列名指定别名 , 别名和java实体类的属性名一致。
<select id="selectUserById" resultType="User">
  select id , name , pwd as password from user where id = #{id}
</select>
  • 使用结果集映射:ResultMap 【推荐】
<resultMap id="UserMap" type="User">
   <!-- id为主键 -->
   <id column="id" property="id"/>
   <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
   <result column="name" property="name"/>
   <result column="pwd" property="password"/>
</resultMap>

<select id="selectUserById" resultMap="UserMap">
  select id , name , pwd from user where id = #{id}
</select>

多对一

多对一最经典的例子就是:学生和老师的关系,对于学生而言,多个学生对应同一个老师。数据库中的表结构如下:
在这里插入图片描述

创建数据表

CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师');

CREATE TABLE `student` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
`tid` INT(10) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `fktid` (`tid`),
CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;


INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1');
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

创建实体类

public class Student {
    private int id;
    private String name;
    private Teacher teacher;
}
public class Teacher {
    private int id;
    private String name;
}

编写Mapper方法

public interface StudentMapper {
    public List<Student> getStudents();
}

编写mapper.xml文件

  • 方法一:按查询嵌套处理
    • 做一个结果集映射:StudentTeacher
    • StudentTeacher结果集的类型type为 Student
    • Student>Teacher对应多对一,association标签中的property对应Student实体类【多】的属性名,column对应数据库中列名,传给下一个sql的字段名,javaType为下一个sql【一】的返回类型,select为下一个sql语句的id。
<mapper namespace="com.yuan.mapper.StudentMapper">
    <select id="getStudents" resultMap="StudentTeacher">
        select * from student
    </select>
    
    <resultMap id="StudentTeacher" type="com.yuan.domain.Student">
        <association property="teacher" column="tid" javaType="com.yuan.domain.Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getTeacher" resultType="com.yuan.domain.Teacher">
        select * from teacher where id = #{id}
    </select>
</mapper>
  • 如果要传递多个字段到下一个sql语句时,格式为column="{key=value,key=value}",key是传给下个sql的取值名称,value是当前sql查询的字段名。比如:
<resultMap id="StudentTeacher" type="Student">
   <!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
   <association property="teacher"  column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
  select * from teacher where id = #{id} and name = #{name}
</select>
  • 方法二:按结果嵌套处理:直接查询出结果,进行结果集的映射
<!--
按查询结果嵌套处理
思路:
   1. 直接查询出结果,进行结果集的映射
-->
<select id="getStudents2" resultMap="StudentTeacher" >
  select s.id sid, s.name sname , t.name tname
  from student s,teacher t
  where s.tid = t.id
</select>

<resultMap id="StudentTeacher" type="Student">
   <id property="id" column="sid"/>
   <result property="name" column="sname"/>
   <!--关联对象property 关联对象在Student实体类中的属性-->
   <association property="teacher" javaType="Teacher">
       <result property="name" column="tname"/>
   </association>
</resultMap>

编写测试类

public class MybatisTest {
    @Test
    public void testGetStudents(){
        SqlSession session = MyBatisUtils.getSession();
        StudentMapper mapper = session.getMapper(StudentMapper.class);
        List<Student> students = mapper.getStudents();
        }
    }
}

一对多

和上面的例子一样,一对多就是从老师的角度来看,一个老师拥有多个学生。
实体类:

public class Student {
   private int id;
   private String name;
   private int tid;
}
public class Teacher {
   private int id;
   private String name;
   //一个老师多个学生
   private List<Student> students;
}

sql语句:
方法一:按查询嵌套处理

<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
   <!--column是一对多的外键 , 写的是一的主键的列名-->
   <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
  select * from student where tid = #{id}
</select>

方法二:按结果嵌套处理

<mapper namespace="com.kuang.mapper.TeacherMapper">

   <!--
   思路:
       1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
       2. 对查询出来的操作做结果集映射
           1. 集合的话,使用collection!
               JavaType和ofType都是用来指定对象类型的
               JavaType是用来指定pojo中属性的类型
               ofType指定的是映射到list集合属性中pojo的类型。
   -->
   <select id="getTeacher" resultMap="TeacherStudent">
      select s.id sid, s.name sname , t.name tname, t.id tid
      from student s,teacher t
      where s.tid = t.id and t.id=#{id}
   </select>

   <resultMap id="TeacherStudent" type="Teacher">
       <result  property="name" column="tname"/>
       <collection property="students" ofType="Student">
           <result property="id" column="sid" />
           <result property="name" column="sname" />
           <result property="tid" column="tid" />
       </collection>
   </resultMap>
</mapper>

总结

  1. 注意属性名和字段不一致的问题
  2. 多对一 用 关联-association
  3. 一对多 用 集合-collection
  4. JavaType和ofType都是用来指定对象类型的,JavaType是用来指定pojo中属性的类型,ofType指定的是映射到list集合属性中pojo的类型。

参考链接:https://mp.weixin.qq.com/s/Dpt0LHTgx0kL7RCk8PZ2aw


版权声明:本文为qq_36895015原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。