resultMap
元素是 MyBatis 中最重要最強大的元素。它可讓你從 90% 的 JDBCResultSets
數據提取代碼中解放出來,並在一些情形下容許你進行一些 JDBC 不支持的操做。實際上,在爲一些好比鏈接的複雜語句編寫映射代碼的時候,一份resultMap
可以代替實現同等功能的數千行代碼。ResultMap 的設計思想是,對簡單的語句作到零配置,對於複雜一點的語句,只須要描述語句之間的關係就好了。
resultMap
能夠將查詢到的複雜數據,好比多張表的數據、一對一映射、一對多映射等複雜關係聚合到一個結果集當中。平常的業務開發一般都會和它打交道,今天就對 resultMap
進行一個詳細講解。java
接下來咱們來看看 resultMap
是如何進行映射的。數據庫
咱們聲明一個數據庫對應的實體類:mybatis
/** * @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
爲:app
<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 方法。this
Getter 和 Setter 方法進行注入是咱們最經常使用的方式。可是 Mybatis 一樣支持構造注入,若是 Employee
存在以下構造方法:spa
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
屬性時會按照構造方法的參數列表順序進行注入。code
在 Mybatis 3.4.3 引入了 name
屬性後咱們就能夠打亂 constructor
標籤內的 arg
元素的順序了。xml
<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>
像 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.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
能夠繼續嵌套下去,有可能關聯的對象中還有一對一關係。
有一對一關聯,天然會有一對多關聯。咱們反客爲主,一個部門有多個員工,咱們可能須要查詢一個部門的信息以及全部員工的信息裝載到 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>
你們都知道,員工並不都是正式工,還有臨時工。有時候咱們也指望可以將這兩種區分開來,至於緣由你懂的。不深刻討論這個問題了。就這個需求而言咱們的映射關係又複雜了,咱們須要根據某個條件來判斷哪條數據是正式工,哪條數據是臨時工,而後分別裝入下面這個實體類的 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="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="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="1"> <collection property="regularEmployees" ofType="cn.felord.mybatis.entity.RegularEmployee"> <!--省略--> </collection> </case> <case value="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>
。
resultMap
可以知足大部分業務場景對於數據映射的需求,今天咱們對 Mybatis 中 resultMap
的一些用法進行了講解,其實 resultMap
還有一些有用的屬性,基於篇幅的緣由這裏再也不講解,可閱讀 Mybatis 官方文檔。可是請注意雖然 resultMap
功能強大,必定要合理使用,級聯過於複雜會影響後期維護和性能。好比當一對多映射時,多的一方若是數據條數過大,會增長內存消耗和讀寫性能。但願今天的文章對你使用 resultMap
有所幫助,更及時的技術資訊請多多關注:碼農小胖哥。
本次文章的 DEMO ,可關注公衆號:Felordcn 回覆 resultMap 獲取。
關注公衆號:Felordcn 獲取更多資訊