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 底層原理
- 索引
- 索引優化