
1. 前言
resultMap元素是 MyBatis 中最重要最強大的元素,它可以讓你從 90% 的 JDBCResultSets資料提取代碼中解放出來,并在一些情形下允許你進行一些 JDBC 不支持的操作,實際上,在為一些比如連接的復雜陳述句撰寫映射代碼的時候,一份resultMap能夠代替實作同等功能的數千行代碼,ResultMap 的設計思想是,對簡單的陳述句做到零配置,對于復雜一點的陳述句,只需要描述陳述句之間的關系就行了,
resultMap 可以將查詢到的復雜資料,比如多張表的資料、一對一映射、一對多映射等復雜關系聚合到一個結果集當中,日常的業務開發通常都會和它打交道,今天就對 resultMap 進行一個詳細講解,
2. resultMap
接下來我們來看看 resultMap 是如何進行映射的,
2.1 Getter/Setter 注入
我們宣告一個資料庫對應的物體類:
/**
* @author felord.cn
* @since 16:50
**/
@Data
public class Employee implements Serializable {
private static final long serialVersionUID = -7145891282327539285L;
private String employeeId;
private String employeeName;
private Integer employeeType;
}
那么它對應的 resultMap 為:
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
<resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="employee_type" property="employeeType"/>
</resultMap>
</mapper>
我們來解釋這些配置的屬性:
<mapper namespace="全域唯一的名稱空間">
<resultMap id="本namespace下唯一" type="對應映射的物體">
<id column="資料庫主鍵欄位名或者別名,使用它提高整體性能" property="對應物體屬性"/>
<result column="資料庫欄位名或者別名" property="對應物體屬性"/>
</resultMap>
</mapper>
以上方式是通過 Getter 和 Setter 方法進行注入,也就是物體類必須有無參構造,對應屬性必須有Getter 和 Setter 方法,
2.2 構造注入
Getter 和 Setter 方法進行注入是我們最常用的方式,但是 Mybatis 同樣支持構造注入,如果 Employee 存在如下構造方法:
public Employee(String employeeId, String employeeName, Integer employeeType) {
this.employeeId = employeeId;
this.employeeName = employeeName;
this.employeeType = employeeType;
}
那么對應的 resultMap 可以這樣寫:
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
<resultMap id="EmployeeMap" type="cn.felord.mybatis.entity.Employee">
<constructor>
<idArg column="employee_id" javaType="String"/>
<arg column="employee_name" javaType="String"/>
<arg column="employee_type" javaType="String"/>
</constructor>
</resultMap>
</mapper>
細心的同學發現這里并沒有 property 屬性,其實當你不宣告property 屬性時會按照構造方法的引數串列順序進行注入,
在 Mybatis 3.4.3 引入了 name 屬性后我們就可以打亂 constructor 標簽內的 arg 元素的順序了,
<mapper namespace="cn.felord.mybatis.mapper.EmployeeMapper">
<resultMap id="EmployeeConstructorMap" type="cn.felord.mybatis.entity.Employee">
<constructor>
<idArg column="employee_id" javaType="String" name="employeeId"/>
<!-- 你可以不按引數串列順序添加-->
<arg column="employee_type" javaType="Integer" name="employeeType"/>
<arg column="employee_name" javaType="String" name="employeeName"/>
</constructor>
</resultMap>
</mapper>
2.3 繼承關系
像 Java 中的類一樣,resultMap 也是可以繼承的,下面是兩個有繼承關系的 Java 類:

那么 RegularEmployee 的 resultMap 就可以這么寫:
<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
<association property="department" javaType="cn.felord.mybatis.entity.Department">
<id column="department_id" property="departmentId"/>
<result column="department_name" property="departmentName"/>
<result column="department_level" property="departmentLevel"/>
</association>
</resultMap>
跟 Java 的繼承關鍵字一樣使用 extends 來進行繼承,
2.4 一對一關聯
明眼人會看出來 2.3 最后一個 resultMap 示例中有一個 association 標簽,這個用來做什么用呢?打個比方,每一個正式員工 RegularEmployee會對應一個部門 Department,業務中會有把這種 一對一 關系查詢出來的需求,所以 association 就派上了用場,
<resultMap id="RegularEmployeeMap" extends="EmployeeMap" type="cn.felord.mybatis.entity.RegularEmployee">
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
<association property="屬性名稱" javaType="對應的Java型別">
<id column="department_id" property="departmentId"/>
<result column="department_name" property="departmentName"/>
<result column="department_level" property="departmentLevel"/>
</association>
</resultMap>
association可以繼續嵌套下去,有可能關聯的物件中還有一對一關系,
2.5 一對多關聯
有一對一關聯,自然會有一對多關聯,我們反客為主,一個部門有多個員工,我們可能需要查詢一個部門的資訊以及所有員工的資訊裝載到 DepartmentAndEmployeeList中去,
/**
* @author felord.cn
* @since 15:33
**/
public class DepartmentAndEmployeeList extends Department {
private static final long serialVersionUID = -2503893191396554581L;
private List<Employee> employees;
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
我們可以在 resultMap 中使用 collection 關鍵字來處理一對多映射關系:
<resultMap id="DepartmentAndEmployeeListMap" extends="DepartmentMap"
type="cn.felord.mybatis.entity.DepartmentAndEmployeeList">
<collection property="employees" ofType="cn.felord.mybatis.entity.RegularEmployee">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
</collection>
</resultMap>
2.6 鑒別器
大家都知道,員工并不都是正式工,還有臨時工,有時候我們也期望能夠將這兩種區分開來,至于原因你懂的,不深入討論這個問題了,就這個需求而言我們的映射關系又復雜了,我們需要根據某個條件來判斷哪條資料是正式工,哪條資料是臨時工,然后分別裝入下面這個物體類的 regularEmployees、temporaryEmployees中,
/**
* @author felord.cn
* @since 15:33
**/
public class DepartmentAndTypeEmployees extends Department {
private static final long serialVersionUID = -2503893191396554581L;
private List<RegularEmployee> regularEmployees;
private List<TemporaryEmployee> temporaryEmployees;
// getter setter
}
鑒別器(discriminator)元素就是被設計來應對這種情況的,另外也能處理其它情況,例如類的繼承層次結構, 鑒別器的概念很好理解——它很像 Java 語言中的 switch 陳述句,
為此我們需要在 Employee 類中增加一個 int型別的 employeeType屬性來區分正式工和臨時工,其中 1代表正式工,而 0代表臨時工,然后我們來撰寫查詢 DepartmentAndTypeEmployees 的 resultMap :
<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap"
type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees">
<collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee">
<discriminator javaType="int" column="employee_type">
<case value="https://www.cnblogs.com/felordcn/p/1">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="employee_type" property="employeeType"/>
<result column="level" property="level"/>
<result column="job_number" property="jobNumber"/>
</case>
</discriminator>
</collection>
<collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
<discriminator javaType="int" column="employee_type">
<case value="https://www.cnblogs.com/felordcn/p/0">
<id column="employee_id" property="employeeId"/>
<result column="employee_name" property="employeeName"/>
<result column="employee_type" property="employeeType"/>
<result column="company_no" property="companyNo"/>
</case>
</discriminator>
</collection>
</resultMap>
切記一定是先宣告 DepartmentAndTypeEmployees的兩個 List ,然后在 collection 標簽內部使用 discriminator 標簽,
這里很容易犯以下錯誤,下面的寫法雖然可以查詢出資料但是滿足不了上述需求:
<resultMap id="DepartmentAndTypeEmployeesMap" extends="DepartmentMap"
type="cn.felord.mybatis.entity.DepartmentAndTypeEmployees">
<discriminator javaType="int" column="employee_type">
<case value="https://www.cnblogs.com/felordcn/p/1">
<collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee">
<!--省略-->
</collection>
</case>
<case value="https://www.cnblogs.com/felordcn/p/0">
<collection property="temporaryEmployees" ofType="cn.felord.mybatis.entity.TemporaryEmployee">
<!--省略-->
</collection>
</case>
</discriminator>
</resultMap>
這種寫法的意思是:當發現該條資料中 employee_type=1 時,就新建一個 List<RegularEmployee> 并把該條資料放進去,每次都會新建一個 List<RegularEmployee> ;當employee_type=0 時也一樣,這樣的話最終就會回傳一個 List<DepartmentAndTypeEmployees> ,
3. 總結
resultMap 能夠滿足大部分業務場景對于資料映射的需求,今天我們對 Mybatis 中 resultMap 的一些用法進行了講解,其實 resultMap 還有一些有用的屬性,基于篇幅的原因這里不再講解,可閱讀 Mybatis 官方檔案,但是請注意雖然 resultMap 功能強大,一定要合理使用,級聯過于復雜會影響后期維護和性能,比如當一對多映射時,多的一方如果資料條數過大,會增加記憶體消耗和讀寫性能,希望今天的文章對你使用 resultMap 有所幫助,更及時的技術資訊請多多關注:碼農小胖哥,
本次文章的 DEMO ,可關注公眾號:Felordcn 回復 resultMap 獲取,
關注公眾號:Felordcn 獲取更多資訊
個人博客:https://felord.cn
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/183579.html
標籤:Java
上一篇:Python 讓我再次在女同學面前長臉了!(真實案例)
下一篇:架構師需要懂的環境配置標準化
