MyBatis學習筆記6、結果集映射ResultMap

6、結果集映射ResultMap

6.1、如果類變量名和字段名不一樣會發生什麼

如果我們把 pojo 類裏的 pwd 改成 passwd。就會導致屬性名和字段名不一致(字段名是pwd)。

private int id;
private String name;
private String passwd;

如果我們執行測試代碼,結果是:

User{id=1, name='dzy', pwd='null'}

獲取不到密碼,原因是 mybatis 把select * from mybatis.user where id = #{id}解析成了

select id, name, pwd from mybatis.user where id = #{id}

pwd 和 passwd 沒有關係,所以passwd不會被賦值。

如果我們修改 sql 爲:

select id, name, pwd as passwd from mybatis.user where id = #{id}

再次執行,結果正確:

User{id=1, name='dzy', pwd='123456'}

6.2、resultMap

上面的在 sql 語句裏起別名是一個解決方法,但是使用resultMap是更好的方法

在 mapper.xml 中使用 resultMap 標籤,如:

<!-- namespace要綁定一個mapper接口 -->
<mapper namespace="com.dzy.dao.UserMapper">
    <!-- 結果集映射 typer="User"是因爲給實體類起了別名所以不寫全路徑-->
    <resultMap id="userMap" type="User">
        <!-- 列是數據庫的字段,property是類的屬性 -->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="passwd"/>
    </resultMap>
    <!-- resultMap和上面的resultMap標籤裏的id一致 -->
    <select id="getUserById" parameterType="int" resultMap="userMap">
        select * from mybatis.user where id = #{id}
    </select>
</mapper>

執行測試,結果正確:

User{id=1, name='dzy', pwd='123456'}
  • resultMap 元素是 MyBatis 中最重要最強大的元素
  • ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。
  • 變量名和字段名一樣的,不需要配置映射,所以前面的xml可以少些兩行。
<mapper namespace="com.dzy.dao.UserMapper">
    <!-- 結果集映射 typer="User"是因爲給實體類起了別名所以不寫全路徑-->
    <resultMap id="userMap" type="User">
        <!-- 列是數據庫的字段,property是類的屬性 -->
        <result column="pwd" property="passwd"/>
    </resultMap>
    <!-- resultMap和上面的resultMap標籤裏的id一致 -->
    <select id="getUserById" parameterType="int" resultMap="userMap">
        select * from mybatis.user where id = #{id}
    </select>
</mapper>

只需要映射 pwd 這個不一樣的,id 和 name 的變量名和字段名一樣,就不用映射了。

6.3、準備多表查詢

首先新建一個 teacher 表

use `mybatis`;
create table `teacher`(
    `id` int(11) auto_increment,
    `name` varchar(30) default null,
    primary key (`id`)
) engine = INNODB default charset = utf8

向 teacher 表中插入數據

insert into mybatis.teacher (`id`, `name`) VALUES (1, '老師A')

再新建一個 student 表

create table `student`(
    `id` int(11) auto_increment,
    `name` varchar(30) default null,
    `tid` int(11) default null,
    primary key (`id`),
    key `fktid` (`tid`),
    constraint `fktid` foreign key (`tid`) references `teacher` (`id`)
)

向 student 表中插入數據

insert into mybatis.student (id, name, tid) VALUES
(1, '小明', 1),
(2, '小紅', 1),
(3, '小張', 1),
(4, '小李', 1),
(5, '小王', 1)

創建實體類 teacher ,使用 lombok 的註解

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
}

創建實體類 student

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    // 直接用引用關聯,int在這裏不合適
    // 因爲int總得有個值,那不關聯應該寫幾呢
    private Teacher teacher;
}

6.4、多對一的處理

剛剛建好的數據表中,多個學生對應一個老師,那麼獲取所有學生及其對應老師就是多對一的查詢

6.4.1、按照查詢嵌套處理

設計接口:

public interface StudentMapper {
    // 查詢所有學生和對應的老師
    public List<Student> getStudents();
}

配置 xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace要綁定一個mapper接口 -->
<mapper namespace="com.dzy.dao.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <!-- id標籤是特殊的result,只能用於主鍵映射,result通用 -->
        <id property="id" column="id" />
        <result property="name" column="name" />
        <!-- result只能映射簡單的類型,複雜的對象要單獨處理 -->
        <!-- 對象使用association,集合使用collection -->
        <!-- javaType是子查詢的返回類型,select是子查詢的id -->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    
    <select id="getStudents" resultMap="studentMap">
        select * from mybatis.student
    </select>

    <select id="getTeacher" resultType="Teacher">
        <!-- 這裏#{}裏可以隨便寫,最好和association標籤裏的colum一致 -->
        select * from mybatis.teacher where id = #{tid}
    </select>

</mapper>

測試:

public class StudentDaoTest {
    @Test
    public void TestGetStudents() {
        try(SqlSession session = MybatisUtils.getSqlSession()) {
            StudentMapper mapper = session.getMapper(StudentMapper.class);
            List<Student> studentList = mapper.getStudents();
            for (Student s : studentList){
                System.out.println(s);
            }
        }
    }
}

結果:

Opening JDBC Connection
Created connection 1423768154.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@54dcfa5a]
==>  Preparing: select * from mybatis.student 
==> Parameters: 
<==    Columns: id, name, tid
<==        Row: 1, 小明, 1
====>  Preparing: select * from mybatis.teacher where id = ? 
====> Parameters: 1(Integer)
<====    Columns: id, name
<====        Row: 1, 老師A
<====      Total: 1
<==        Row: 2, 小紅, 1
<==        Row: 3, 小張, 1
<==        Row: 4, 小李, 1
<==        Row: 5, 小王, 1
<==      Total: 5
Student(id=1, name=小明, teacher=Teacher(id=1, name=老師A))
Student(id=2, name=小紅, teacher=Teacher(id=1, name=老師A))
Student(id=3, name=小張, teacher=Teacher(id=1, name=老師A))
Student(id=4, name=小李, teacher=Teacher(id=1, name=老師A))
Student(id=5, name=小王, teacher=Teacher(id=1, name=老師A))
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@54dcfa5a]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@54dcfa5a]
Returned connection 1423768154 to pool.

6.4.2、按照結果嵌套處理

<mapper namespace="com.dzy.dao.StudentMapper">
    <resultMap id="studentMap" type="Student">
        <!-- id標籤是特殊的result,只能用於主鍵映射,result通用 -->
        <id property="id" column="sid" />
        <result property="name" column="sn" />
        <!-- result只能映射簡單的類型,複雜的對象要單獨處理 -->
        <!-- 對象使用association,集合使用collection -->
        <!-- javaType是子查詢的返回類型,select是子查詢的id -->
        <association property="teacher" javaType="Teacher">
            <id property="id" column="tid"/>
            <result property="name" column="tn" />
        </association>
    </resultMap>
    
    <select id="getStudents" resultMap="studentMap">
        select s.id sid, s.name sn, t.id tid, t.name tn from student s, teacher t where s.tid = t.id
    </select>

</mapper>

這種方法只需要一個 sql,但是association標籤裏要寫的東西多一些。

之前的方法對應子查詢,這種方法對應聯表查詢。

6.5、一對多處理

6.5.1、按結果(聯表)

首先修改實體類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;
    // 直接用引用關聯,int在這裏不合適
    // 因爲int總得有個值,那不關聯應該寫幾呢
    private Teacher teacher;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
    // 一個老師關聯多個學生
    private List<Student> studentList;
}

然後是 DAO 接口 TeacherMapper.java

public interface TeacherMapper {
    Teacher getTeacherById(@Param("tid") int id);
}

然後是 TeacherMapper.xml

<mapper namespace="com.dzy.dao.TeacherMapper">

    <resultMap id="teacherMap" type="Teacher">
        <id property="id" column="tid" />
        <result property="name" column="tn"/>
        <!-- 集合中的泛型使用ofType -->
        <collection property="studentList" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sn"/>
        </collection>
    </resultMap>

    <select id="getTeacherById" resultMap="teacherMap">
        select t.id tid, t.name tn, s.id sid, s.name sn from teacher t, student s where t.id = s.tid and t.id = #{tid}
    </select>
</mapper>

最後是測試類

public class TeacherDaoTest {
    @Test
    public void testSelectTeacherByID() {
        try (SqlSession session = MybatisUtils.getSqlSession()) {
            TeacherMapper mapper = session.getMapper(TeacherMapper.class);
            Teacher teacher = mapper.getTeacherById(1);
            System.out.println(teacher);
        }
    }
}

輸出結果爲

Teacher(id=1, name=老師A, 
		studentList=[Student(id=1, name=小明, teacher=null), 	    
        Student(id=2, name=小紅, teacher=null), 
        Student(id=3, name=小張, teacher=null), 
        Student(id=4, name=小李, teacher=null), 
        Student(id=5, name=小王, teacher=null)])

所以,實體類完全可以相互持有

6.5.2、按查詢(子查詢)

只需要修改 TeacherMapper.xml 爲:

<mapper namespace="com.dzy.dao.TeacherMapper">

    <resultMap id="teacherMap" type="Teacher">
        <id property="id" column="id" />
        <result property="name" column="name"/>
        <!-- javaType是集合的類型,ofType是集合裏的元素的類型 -->
        <!-- colunm是傳入參數的列名,絕對不能錯 -->
        <collection property="studentList" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTid"/>
    </resultMap>

    <select id="getTeacherById" resultMap="teacherMap">
        select * from teacher where id = #{tid}
    </select>
    
    <select id="getStudentByTid" parameterType="int" resultType="Student">
        select * from student where tid = #{tid}
    </select>
</mapper>

返回結果是一樣的

一般選擇**按結果(聯表)**的方法,因爲只有一個 sql 方便調試。

6.6、小結

  • association & collection
  • javaType & ofType
  • property & column
  • 多個 sql & 一個 sql

高頻問題:

  • MySQL 引擎
  • InnoDB 底層原理
  • 索引
  • 索引優化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章