1. 程式人生 > >Mybatis系列(八)集合對映

Mybatis系列(八)集合對映

package com.emerson.learning.pojo;

import java.sql.Timestamp;

public class Customer {

	/**
	 * 
	 */
	private int customerId;

	/**
	 * 
	 */
	private String customerName;

	/**
	 * 
	 */
	private int isValid;

	/**
	 * 
	 */
	private Timestamp createdTime;

	/**
	 * 
	 */
	private Timestamp updateTime;

	/**
	 * 
	 */
	private User userInfo;

	@Override
	public String toString() {
		return "Customer [customerId=" + customerId + ", customerName=" + customerName + ", isValid=" + isValid
				+ ", createdTime=" + createdTime + ", updateTime=" + updateTime + ", userInfo=" + userInfo + "]";
	}

	public int getCustomerId() {
		return customerId;
	}

	public void setCustomerId(int customerId) {
		this.customerId = customerId;
	}

	public String getCustomerName() {
		return customerName;
	}

	public void setCustomerName(String customerName) {
		this.customerName = customerName;
	}

	public int getIsValid() {
		return isValid;
	}

	public void setIsValid(int isValid) {
		this.isValid = isValid;
	}

	public Timestamp getCreatedTime() {
		return createdTime;
	}

	public void setCreatedTime(Timestamp createdTime) {
		this.createdTime = createdTime;
	}

	public Timestamp getUpdateTime() {
		return updateTime;
	}

	public void setUpdateTime(Timestamp updateTime) {
		this.updateTime = updateTime;
	}

	public User getUserInfo() {
		return userInfo;
	}

	public void setUserInfo(User userInfo) {
		this.userInfo = userInfo;
	}
}

 第二步,修改對映檔案。我們先使用巢狀查詢方式來實現為銷售載入客戶列表。首先在resultMap中增加客戶集合對映的定義。

巢狀查詢 

<!-- 定義一對多集合資訊(每個銷售人員對應多個客戶) -->
<collection property="customers" javaType="ArrayList" column="sales_id" ofType="Customer" select="getCustomerForSales" />

集合對映的定義與關聯對映定義很相似,除了關鍵字不同外,還多了兩個屬性JavaType和ofType。

property用於指定在Java實體類是儲存集合關係的屬性名稱

JavaType用於指定在Java實體類中使用什麼型別來儲存集合資料,多數情況下這個屬性可以省略的。

column用於指定資料表中的外來鍵欄位名稱。

ofType用於指定集合中包含的型別。

select用於指定查詢語句。

然後再定義查詢客戶的查詢語句。

<select id="getCustomerForSales" resultType="com.emerson.learning.pojo.Customer">
    SELECT c.customer_id, c.customer_name, c.user_id, c.is_valid,
    c.created_time, c.update_time
    FROM customer c INNER JOIN customer_sales s USING(customer_id)
    WHERE s.sales_id = #{id}
</select>

需要注意的是,無論是關聯還是集合,在巢狀查詢的時候,查詢語句的定義都不需要使用parameterType屬性定義傳入的引數型別,因為通常作為外來鍵的,都是簡單資料型別,查詢語句會自動使用定義在association或是collection元素上column屬性作為傳入引數的。

執行測試用例,看到如下結果就說明我們的對映檔案是正確的了。
Opening JDBC Connection
Created connection 632249781.
Setting autocommit to false on JDBC Connection [[email protected]]
==>  Preparing: SELECT sales_id, sales_name, sales_phone, sales_fax, sales_email, salesman.is_valid, salesman.created_time, salesman.update_time, sys_user.user_id as user_id, user_name, user_password, nick_name, email as user_email, sys_user.is_valid as user_is_valid, sys_user.created_time as user_created_time, sys_user.update_time as user_update_time FROM salesman left outer join sys_user using(user_id) WHERE sales_id=? 
==> Parameters: 2(Integer)
<==    Columns: sales_id, sales_name, sales_phone, sales_fax, sales_email, is_valid, created_time, update_time, user_id, user_name, user_password, nick_name, user_email, user_is_valid, user_created_time, user_update_time
<==        Row: 2, Bing Gu, 021-3418 1999, null, [email protected], 1, 2015-10-01 20:21:26.0, 2015-10-01 21:56:40.0, 25, binggu, 5f4dcc3b5aa765d61d8327deb882cf99, Bing Gu, null, 1, 2015-09-30 22:04:34.0, 2015-09-30 22:04:34.0
====>  Preparing: SELECT c.customer_id, c.customer_name, c.user_id, c.is_valid, c.created_time, c.update_time FROM customer c INNER JOIN customer_sales s USING(customer_id) WHERE s.sales_id = ? 
====> Parameters: 2(Integer)
<====    Columns: customer_id, customer_name, user_id, is_valid, created_time, update_time
<====        Row: 161, 客戶1, null, 1, 2015-10-01 20:24:05.0, 2015-10-01 20:24:05.0
<====        Row: 163, 客戶2, null, 1, 2015-10-01 20:24:05.0, 2015-10-01 20:24:05.0
<====        Row: 164, 客戶3, null, 1, 2015-10-01 20:24:05.0, 2015-10-01 20:24:05.0
<====      Total: 3
<==      Total: 1

我們看到這裡與資料庫進行了兩查詢互動,說明這裡仍然存在著“N+1”的問題。下面,我們改用巢狀結果的方式來實現銷售與客戶的對映關係。

巢狀結果

<resultMap id="salesResultMap" type="com.emerson.learning.pojo.Sales">
    <id property="salesId" column="sales_id" />
    <result property="salesName" column="sales_name" />
    <result property="phone" column="sales_phone" />
    <result property="fax" column="sales_fax" />
    <result property="email" column="sales_email" />
    <result property="isValid" column="is_valid" />
    <result property="createdTime" column="created_time" />
    <result property="updateTime" column="update_time" />

    <!-- 定義多對一關聯資訊(巢狀結果方式) -->
    <association property="userInfo" resultMap="userResult" />

    <!-- 定義一對多集合資訊(每個銷售人員對應多個客戶) -->
    <!-- <collection property="customers" column="sales_id" select="getCustomerForSales" /> -->

    <collection property="customers" ofType="com.emerson.learning.pojo.Customer">
        <id property="customerId" column="customer_id" />
        <result property="customerName" column="customer_name" />
        <result property="isValid" column="is_valid" />
        <result property="createdTime" column="created_time" />
        <result property="updateTime" column="update_time" />
        <!-- 對映客戶與登入使用者的關聯關係,請注意columnPrefix屬性 -->
        <association property="userInfo" resultMap="userResult" columnPrefix="cu_" />
    </collection>
</resultMap>

這裡將客戶的對映關係直接寫在了銷售的resultMap中。上述程式碼與關聯對映十分相似,只是有一點需要朋友們留心,那就是在對客戶資料進行對映的時候,我們使用了association元素的一個新的屬性columnPrefix。這個屬性是做什麼用的呢?從名字上理解,就是給每個欄位之前加上字首。Bingo!答對了,那麼什麼情況下會使用到這個屬性呢?後面我們會結合著修改後的查詢語句來說明這個屬性的使用場景。請耐心的往下看。:)

對映結果修改好了,緊接著我們就要修改查詢語句了。

<select id="getById" parameterType="int" resultMap="salesResultMap">
    SELECT
        s.sales_id, s.sales_name, s.sales_phone, s.sales_fax, s.sales_email,
        s.is_valid, s.created_time, s.update_time,
        su.user_id as user_id, su.user_name, su.user_password, su.nick_name,
        su.email as user_email,
        su.is_valid as user_is_valid,
        su.created_time as user_created_time,
        su.update_time as user_update_time,
        c.customer_id, c.customer_name, c.is_valid as customer_is_valid,
        c.created_time as customer_created_time,
        c.update_time as customer_update_time,
        cu.user_id as cu_user_id, cu.user_name as cu_user_name, cu.user_password as cu_user_password, 
        cu.nick_name as cu_nick_name, cu.email as cu_user_email, cu.is_valid as cu_user_is_valid,
        cu.created_time as cu_user_created_time, cu.update_time as cu_user_update_time
    FROM
        salesman s LEFT OUTER JOIN sys_user su ON s.user_id = su.user_id
        INNER JOIN customer_sales cs USING(sales_id)
        LEFT OUTER JOIN customer c USING(customer_id)
        LEFT OUTER JOIN sys_user cu ON c.user_id = cu.user_id
    WHERE sales_id=#{id}
</select>

這個語句乍看起來有些複雜,其實很容易理解。這裡用到了四張資料表,銷售、客戶、客房銷售關係表和登入使用者表。具體的欄位我就不說了,主要說一下這個登入使用者表。這張資料表在查詢語句中出現了兩次,為什麼呢?因為銷售與登入使用者有關聯關係,同樣地,客戶也與登入使用者表有關聯關係,所以我們需要對使用者表進行兩次Join操作。

那麼問題來了,銷售要使用者有關聯,客戶也要與使用者有關聯,這種對映語句應該如何寫呢?難道要對使用者表寫兩次對映?聰明的朋友一定會說,我們可以複用之前寫過的使用者對映結果集呀!答案是肯定的。我們不妨在這裡再次貼出這段程式碼,一起回憶一下。

<resultMap id="userResult" type="User">
    <id property="userId" column="user_id" />
    <result property="userName" column="user_name" />
    <result property="userPassword" column="user_password" />
    <result property="nickName" column="nick_name" />
    <result property="email" column="user_email" />
    <result property="isValid" column="user_is_valid" />
    <result property="createdTime" column="user_created_time" />
    <result property="updateTime" column="user_update_time" />
</resultMap>

資料表中的欄位與Java實體類中的屬性的對映關係是一一對應的,Mybatis會根據我們定義的對映關係,將資料表中欄位的對映到Java實體類屬性上。

可是我們的查詢語句中對使用者表進行了兩次Join操作,第一次是銷售與使用者的Join,第二次是客戶與使用者的Join。而SQL語句是不允許在同一條查詢語句中出現相同欄位名的(雖然我們有時候會這樣寫,但是資料庫會自動幫我們為重名的欄位名起個別名的,比如在欄位名後新增數字)。如果我們為第二次Join進來的使用者表中的欄位使用別名方式,那麼就會導致對映的到客戶類中的使用者資訊缺失,因為欄位名與我們在對映檔案中的定義不一致。如何解決這個問題呢?這時候該columnPrefix屬性出場了。

Mybatis也考慮到這種情況的出現,她允許我們在重複出現的欄位名前加上一個統一的字元字首,這樣就可以有效的避免欄位重名,又可以複用之前定義的對映結果集。

在上述的查詢語句中,我們為第二次Join進來的使用者表中的欄位都加上了“cu_”做為區分重名欄位的字首,同時使用columnPrefix屬性告訴Mybatis在第二次對使用者表對映的時候,將欄位名是以“cu_”打頭的欄位值對映到Java實體類屬性當中。這樣就可以正確的把客戶與使用者的關聯資訊對映到Customer物件當中了。

<association property="userInfo" resultMap="userResult" columnPrefix="cu_" />

上述的表達可能有些臃腫,不知道小夥朋友們明白了沒有。理工男的寫作水平,你們懂的。

彩蛋奉上(共享不同對映檔案中的結果集)

我們之前在User.xml檔案中定義過使用者表的對映結果集,現在在Sales.xml中也需要使用到同樣的結果集,是否可以直接跨檔案引用呢?答案是肯定的了,不然對於同一個對映結果集,我們要多處編寫,多處維護,這樣不僅工作量大,對日後的維護也帶來了一定的麻煩。我們只需要在引用處使用結果集的全限定名就可以了。

	<resultMap id="salesResultMap" type="com.emerson.learning.pojo.Sales">
		<id property="salesId" column="sales_id" />
		<result property="salesName" column="sales_name" />
		<result property="phone" column="sales_phone" />
		<result property="fax" column="sales_fax" />
		<result property="email" column="sales_email" />
		<result property="isValid" column="is_valid" />
		<result property="createdTime" column="created_time" />
		<result property="updateTime" column="update_time" />

		<!-- 定義多對一關聯資訊(巢狀查詢方式) -->
		<!-- <association property="userInfo" column="user_id" javaType="User" 
			select="selectUser" fetchType="lazy"> </association> -->

		<!-- 定義多對一關聯資訊(巢狀結果方式) -->
		<association property="userInfo" resultMap="com.emerson.learning.xml.user.userResult" />

		<!-- 定義一對多集合資訊(每個銷售人員對應多個客戶) -->
		<!-- <collection property="customers" column="sales_id" select="getCustomerForSales" 
			/> -->

		<collection property="customers" ofType="com.emerson.learning.pojo.Customer">
			<id property="customerId" column="customer_id" />
			<result property="customerName" column="customer_name" />
			<result property="isValid" column="is_valid" />
			<result property="createdTime" column="created_time" />
			<result property="updateTime" column="update_time" />
			<association property="userInfo" resultMap="com.emerson.learning.xml.user.userResult" columnPrefix="cu_" />
		</collection>
	</resultMap>

附錄


相關推薦

Mybatis系列集合對映

package com.emerson.learning.pojo; import java.sql.Timestamp; public class Customer { /** * */ private int customerId; /** * */ private S

MyBatis的學習——關聯對映之多對多關聯

本次多對多關聯在之前進行的一對一和一對多關聯基礎之上進行。 需求:根據班級課程查詢選修學生資訊 新建課程表和課程表與學生表的中間表 建立Course實體類: package com.little.entity; import java.util.ArrayL

SpringBoot 學習系列 | IDEA Mybatis 生成逆向工程generator

本篇將主要介紹Mybatis的逆向工程如何在SpringBoot環境上實現。 環境準備        IDEA、SpringBoot、Mybatis  目錄結構     表結構 maven依賴的包與外掛(只貼出Mybatis相關包) <!--mysql資

深入淺出Mybatis高階對映

    前面的章節使用的都是對單張表的操作,但是在實際開發當中,很多時候需要關聯多張表,這時就需要用到我們的高階對映,包括一對一,一對多和多對多。下面先來分析一個簡單的業務場景。有四張表:使用者表:user儲存使用者相關資訊。CREATE TABLE `user` ( `

backbone入門系列4集合

src 單獨 -1 lec 指定 one js代碼 bsp 技術 collection就是一堆model的集合,這個集合就是個舞臺,可以放一個人說單口相聲,也可以對口,也可以群口,,, 在前文,也就是入門系列3的基礎上,添加js代碼 var noteCollection=B

uml系列——部署圖與構件圖

復雜 數據 net 打包 img 之前 說明 而且 bsp 之前說了uml的設計圖,現在說一下uml的最後兩種圖:構件圖、部署圖。這兩種圖之所以放在一起是因為它們都是軟件的實現圖。 構件圖 構件圖是描述一組構件之間

java集合

java 集合類 MapMap<K,V>:Map集合一次添加一對元素,Collection一次添加一個元素。 所以Map集合也稱為雙列集合,而Collection稱為單列集合。 其實Map集合存儲的就是鍵值對。 Map集合必須保證鍵的唯一性。常用方法:1.添加:

SpringMVC系列國際化

enc undle charset ucc tid utf pre 獲取值 -c 1.在pom.xml引入國際化需要的依賴 1 <!--國際化相關依賴 begin --> 2 <dependency> 3 <groupI

深入淺出Mybatis系列---SQL執行流程分析源碼篇(轉)

factor demo 讀取配置 gist wrapper load 任性 wrap 深入淺出 轉載自:http://www.cnblogs.com/dongying/p/4142476.html 1. SqlSessionFactory 與 SqlSession.   通

深入淺出Mybatis系列---強大的動態SQL

tool 復制代碼 otherwise strong sql語句 src sep des col   傳統的使用JDBC的方法,相信大家在組合復雜的的SQL語句的時候,需要去拼接,稍不註意哪怕少了個空格,都會導致錯誤。Mybatis的動態SQL功能正是為了解決這種問題

winform 寫App.config配置文件——IT輪子系列

項目 ble .exe private conf 遇到 配置信息 操作 src 前言 在winform項目中,常常需要讀app.config文件。如: 1 var version = System.Configuration.ConfigurationManager.Ap

Docker入門與應用系列Docker圖形界面管理之Shipyard

tps 數據庫 sock blog ocs body mage 代理 cell Shipyard基於Docker API實現的容器圖形管理系統,支持container、images、engine、cluster等功能,可滿足我們基本的容器部署需求可堆棧的Docker管理基於

SQL系列—— 分組group by

出了 常用 sql select group 數量 通過 報錯 mysql 在很多場景時,需要對數據按照某條件進行分組統計其數量、平均值等等。有這種需求,SQL自然也有解決方式。 在SQL中通過group by子句對結果按某條件進行分組。語法: select count(c

Mybatis系列配置

alt ctype 支持 property 1.0 efault 事務 sla doctype Mybatis系列(二)配置 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration P

Mybatis系列示例

acl apu 操作 model format cal efault ESS json轉換 Mybatis系列(三)示例 1.pom.xml依賴: <?xml version="1.0" encoding="UTF-8"?> <project xmlns

Mybatis系列註解

osi nap repos except nec javax art 5.1 aging Mybatis系列(四)註解 1.pom.xlm: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h

MyBatis學習

c2c 延遲 bat 文件 銷售部 kingdom ati %type eset 本教程對應視頻課程地址:http://edu.51cto.com/sd/3ec2c 1、延遲加載 延遲加載的意義在於,雖然是關聯查詢,但是不是及時將關聯的數據查詢出來,而是在需要的時候進行查詢

深入淺出Mybatis系列---TypeHandler簡介及配置mybatis原始碼篇

上篇文章《深入淺出Mybatis系列(四)---配置詳解之typeAliases別名(mybatis原始碼篇)》為大家介紹了mybatis中別名的使用,以及其原始碼。本篇將為大家介紹TypeHandler, 並簡單分析其原始碼。 Mybatis中的TypeHandler是什麼?   無論是 My

Mybatis總結3-關係對映

關係對映的理解:就像我們每個人之間的人際關係一樣,兒子是父親的兒子也是爺爺的孫子,父親是母親的丈夫是爺爺的兒子這樣的人際關係組成一張關係網構成一張對映網,就像我們資料庫中的表一樣互相都有可能是這樣的關係。所以我們需要在做開發的時候將這種關係對映(獲得爺爺的後代)的時候就需要用到我們的

apache ignite系列:問題彙總

1,java.lang.ClassNotFoundException Unknown pair 1.Please try to turn on isStoreKeepBinary in cache settings - like this; please note the last line: down