mybatis 一对多_Mybatis 强大的结果集映射器resultMap
1. 前言
resultMap 元素是 MyBatis 中最重要最強(qiáng)大的元素。它可以讓你從 90% 的 JDBC ResultSets 數(shù)據(jù)提取代碼中解放出來(lái),并在一些情形下允許你進(jìn)行一些 JDBC 不支持的操作。實(shí)際上,在為一些比如連接的復(fù)雜語(yǔ)句編寫(xiě)映射代碼的時(shí)候,一份 resultMap 能夠代替實(shí)現(xiàn)同等功能的數(shù)千行代碼。ResultMap 的設(shè)計(jì)思想是,對(duì)簡(jiǎn)單的語(yǔ)句做到零配置,對(duì)于復(fù)雜一點(diǎn)的語(yǔ)句,只需要描述語(yǔ)句之間的關(guān)系就行了。resultMap 可以將查詢到的復(fù)雜數(shù)據(jù),比如多張表的數(shù)據(jù)、一對(duì)一映射、一對(duì)多映射等復(fù)雜關(guān)系聚合到一個(gè)結(jié)果集當(dāng)中。日常的業(yè)務(wù)開(kāi)發(fā)通常都會(huì)和它打交道,今天就對(duì) resultMap 進(jìn)行一個(gè)詳細(xì)講解。
2. resultMap
接下來(lái)我們來(lái)看看 resultMap 是如何進(jìn)行映射的。
2.1 Getter/Setter 注入
我們聲明一個(gè)數(shù)據(jù)庫(kù)對(duì)應(yīng)的實(shí)體類:
/*** @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; }那么它對(duì)應(yīng)的 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>我們來(lái)解釋這些配置的屬性:
<mapper namespace="全局唯一的名稱空間"><resultMap id="本namespace下唯一" type="對(duì)應(yīng)映射的實(shí)體"><id column="數(shù)據(jù)庫(kù)主鍵字段名或者別名,使用它提高整體性能" property="對(duì)應(yīng)實(shí)體屬性"/><result column="數(shù)據(jù)庫(kù)字段名或者別名" property="對(duì)應(yīng)實(shí)體屬性"/></resultMap> </mapper>以上方式是通過(guò) Getter 和 Setter 方法進(jìn)行注入,也就是實(shí)體類必須有無(wú)參構(gòu)造,對(duì)應(yīng)屬性必須有Getter 和 Setter 方法。
2.2 構(gòu)造注入
Getter 和 Setter 方法進(jìn)行注入是我們最常用的方式。但是 Mybatis 同樣支持構(gòu)造注入,如果 Employee 存在如下構(gòu)造方法:
public Employee(String employeeId, String employeeName, Integer employeeType) {this.employeeId = employeeId;this.employeeName = employeeName;this.employeeType = employeeType; }那么對(duì)應(yīng)的 resultMap 可以這樣寫(xiě):
<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>細(xì)心的同學(xué)發(fā)現(xiàn)這里并沒(méi)有 property 屬性,其實(shí)當(dāng)你不聲明property 屬性時(shí)會(huì)按照構(gòu)造方法的參數(shù)列表順序進(jìn)行注入。
在 Mybatis 3.4.3 引入了 name 屬性后我們就可以打亂 constructor 標(biāo)簽內(nèi)的 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"/><!-- 你可以不按參數(shù)列表順序添加--><arg column="employee_type" javaType="Integer" name="employeeType"/><arg column="employee_name" javaType="String" name="employeeName"/></constructor></resultMap> </mapper>2.3 繼承關(guān)系
像 Java 中的類一樣,resultMap 也是可以繼承的。下面是兩個(gè)有繼承關(guān)系的 Java 類:
那么 RegularEmployee 的 resultMap 就可以這么寫(xiě):
<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 的繼承關(guān)鍵字一樣使用 extends 來(lái)進(jìn)行繼承。
2.4 一對(duì)一關(guān)聯(lián)
明眼人會(huì)看出來(lái) 2.3 最后一個(gè) resultMap 示例中有一個(gè) association 標(biāo)簽。這個(gè)用來(lái)做什么用呢?打個(gè)比方,每一個(gè)正式員工 RegularEmployee會(huì)對(duì)應(yīng)一個(gè)部門(mén) Department,業(yè)務(wù)中會(huì)有把這種 一對(duì)一 關(guān)系查詢出來(lái)的需求。所以 association 就派上了用場(chǎng)。
<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="對(duì)應(yīng)的Java類型"><id column="department_id" property="departmentId"/><result column="department_name" property="departmentName"/><result column="department_level" property="departmentLevel"/></association> </resultMap>association 可以繼續(xù)嵌套下去,有可能關(guān)聯(lián)的對(duì)象中還有一對(duì)一關(guān)系。2.5 一對(duì)多關(guān)聯(lián)
有一對(duì)一關(guān)聯(lián),自然會(huì)有一對(duì)多關(guān)聯(lián)。我們反客為主,一個(gè)部門(mén)有多個(gè)員工,我們可能需要查詢一個(gè)部門(mén)的信息以及所有員工的信息裝載到 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 關(guān)鍵字來(lái)處理一對(duì)多映射關(guān)系:
<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 鑒別器
大家都知道,員工并不都是正式工,還有臨時(shí)工。有時(shí)候我們也期望能夠?qū)⑦@兩種區(qū)分開(kāi)來(lái),至于原因你懂的。不深入討論這個(gè)問(wèn)題了。就這個(gè)需求而言我們的映射關(guān)系又復(fù)雜了,我們需要根據(jù)某個(gè)條件來(lái)判斷哪條數(shù)據(jù)是正式工,哪條數(shù)據(jù)是臨時(shí)工,然后分別裝入下面這個(gè)實(shí)體類的 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)元素就是被設(shè)計(jì)來(lái)應(yīng)對(duì)這種情況的,另外也能處理其它情況,例如類的繼承層次結(jié)構(gòu)。 鑒別器的概念很好理解——它很像 Java 語(yǔ)言中的 switch 語(yǔ)句。
為此我們需要在 Employee 類中增加一個(gè) int類型的 employeeType屬性來(lái)區(qū)分正式工和臨時(shí)工,其中 1代表正式工,而 0代表臨時(shí)工。然后我們來(lái)編寫(xiě)查詢 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的兩個(gè) List ,然后在 collection 標(biāo)簽內(nèi)部使用 discriminator 標(biāo)簽。
這里很容易犯以下錯(cuò)誤,下面的寫(xiě)法雖然可以查詢出數(shù)據(jù)但是滿足不了上述需求:
<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>這種寫(xiě)法的意思是:當(dāng)發(fā)現(xiàn)該條數(shù)據(jù)中 employee_type=1 時(shí),就新建一個(gè) List<RegularEmployee> 并把該條數(shù)據(jù)放進(jìn)去,每次都會(huì)新建一個(gè) List<RegularEmployee> ;當(dāng)employee_type=0 時(shí)也一樣。這樣的話最終就會(huì)返回一個(gè) List<DepartmentAndTypeEmployees> 。
3. 總結(jié)
resultMap 能夠滿足大部分業(yè)務(wù)場(chǎng)景對(duì)于數(shù)據(jù)映射的需求,今天我們對(duì) Mybatis 中 resultMap 的一些用法進(jìn)行了講解,其實(shí) resultMap 還有一些有用的屬性,基于篇幅的原因這里不再講解,可閱讀 Mybatis 官方文檔。但是請(qǐng)注意雖然 resultMap 功能強(qiáng)大,一定要合理使用,級(jí)聯(lián)過(guò)于復(fù)雜會(huì)影響后期維護(hù)和性能。比如當(dāng)一對(duì)多映射時(shí),多的一方如果數(shù)據(jù)條數(shù)過(guò)大,會(huì)增加內(nèi)存消耗和讀寫(xiě)性能。希望今天的文章對(duì)你使用 resultMap 有所幫助,更及時(shí)的技術(shù)資訊請(qǐng)多多關(guān)注:碼農(nóng)小胖哥。
文章的DEMO
關(guān)注微信公眾號(hào):Felordcn 獲取更多干貨
總結(jié)
以上是生活随笔為你收集整理的mybatis 一对多_Mybatis 强大的结果集映射器resultMap的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: JQuery 加载 CSS、JS 文件
- 下一篇: 如何open一个新tab页面