MyBatis 使用resultMap 以及 一對一和一對多

weiewiyi發表於2022-12-01

記錄一下從hibernate 改成 mybatis 遇到的一些問題

小問題一

執行的時候突然發現的報錯:

lALPJwKt15YT0yHNAbzNB00_1869_444.png

檢查過幾遍之後才發現原因: 之前的時候不小心在後面增加了一個斜槓

lQLPJxbo6TArbmbNAdbNA0SwNaEyiX1gdWwDfqrhxcBAAA_836_470.png

由於編譯器不會對 sql 語句進行檢查, 所以執行後臺的時候並沒有報錯。所以當時沒發現。

而過幾天之後,當執行到這條sql 語句的時候, 後臺才報了錯。

所以,我們寫sql 語句的時候要小心。

以防我們寫的不對, 還可以進入mysql 執行一下這條語句看看結果。

image.png

發現問題

背景: 分頁的時候發現這三列資料為空

效果圖:
lQLPJxbo6TH1MzPNAVXNAnWw2qQNvPSVSDMDfqrk1oDsAA_629_341.png

排查問題:

內部編號這一列的問題很快就排查了出來:

後臺與資料庫欄位名不一致

資料庫:
image.png


後臺實體:
image.png

所以在我們執行下面這條語句時,在進行實體對映的時候對應不上, 所以為null

 <select id="findAll" resultType="equipmentManagementSystem.entity.Equipment">
        select * from equipment
  </select>

以往的解決辦法

我想起了以前 前後臺欄位不對應時 的解決辦法:

@JsonProperty("internal_number")
private String internalNumber;

然而這個註釋並沒有用。

因為這個註解是json序列化時,序列化為另一個名稱。 前後臺就是透過 json 傳遞資料資訊。

而目前情況是後臺與資料庫的互動, ORM 把資料庫對映成物件, 用不到Json。

所以此時我們需要用到myBatis的另一個標籤 resultMap

resultMap

resultMap屬於mybatis返回操作結果的一個標籤,可以用來對映select查詢出來結果的集合。

主要作用是將實體類中的欄位與資料庫表中的欄位進行關聯對映

resultMap 屬性:

屬性描述
id當前名稱空間中的一個唯一標識,用於標識一個結果對映。
type類的完全限定名
extends可以繼承其他resultMap的一些寫好的屬性

result屬性:

屬性描述
id一個 ID 結果;標記出作為 ID 的結果可以幫助提高整體效能
result注入到欄位或 JavaBean 屬性的普通結果
association一個複雜型別的關聯;許多結果將包裝成這種型別
collection一個複雜型別的集合

例子:

<mapper namespace="equipmentManagementSystem.Mybatis.EquipmentMapper">
    <!--type指向javabean類,id可自定義 -->
        <resultMap id="equipmentMap" type="equipmentManagementSystem.entity.Equipment">
            <id column="id" property="id"></id>
             <!-- property對應實體類的屬性名稱,column為資料庫欄位名 -->
            <result column="name" property="name"/>
            <result column="model" property="model"/>
            <result column="place" property="place"/>
            <result column="states" property="states"/>
            <result column="sale_time" property="saleTime"/>
            <result column="internal_number" property="internalNumber"/>
        </resultMap>
    <select id="findAll" resultMap="equipmentMap">
        select * from equipment
    </select>
</mapper>

這個時候, 我們就可以在 column 屬性寫上 實體名,在 property 屬性寫上資料庫名。
最後就對映成功。在列表中可以看到這列。

image.png

-

剩下兩列還為空。 原因是: 剩下兩個屬性都是物件

image.png

image.png

因為我們在資料庫中存的是 主鍵

所以我們沒辦法使用, result標籤來進行簡單對映。 否則, 我們只得到的是type_id 這個欄位, 而不是 一個物件

<result column="type_id" property="tyoe"/>

這時候就需要用到上面提到過的 association 標籤了。

association 一對一

它其實就相當於: 一對一OneToOne

用法有兩種:

1. 巢狀結果 resultMap

第5行是重點, 和result標籤一樣的,property 代表 java 實體的屬性名, column 代表的 資料庫的欄位名。

resultMap 屬性值,給的是該物件對應的resultMap。

簡單來說,我們用一個resultMap 對映了該屬性物件。讓myBatis幫我們自動裝配物件

1   <resultMap id="equipmentMap" type="equipmentManagementSystem.entity.Equipment">
2            <!-- property對應實體類的屬性名稱,column為資料庫欄位名 -->
3            <id column="id" property="id"></id>
4            <result column="internal_number" property="internalNumber"/>
5            <association property="type" column="type_id" resultMap="equipmentManagementSystem.Mybatis.TypeMapper.typeResultMap"/>
6        </resultMap>
7    <select id="findAll" resultMap="equipmentMap">
8        select * from equipment
9    </select>

image.png

2. 巢狀查詢 select

image.png

1 <resultMap id="equipmentMap" type="equipmentManagementSystem.entity.Equipment">
2    <!-- property對應實體類的屬性名稱,column為資料庫欄位名 -->
3    <id column="id" property="id"></id>
4    <result column="sale_time" property="saleTime"/>
5    <result column="internal_number" property="internalNumber"/>
6    <association property="type" column="type_id" select="equipmentManagementSystem.Mybatis.TypeMapper.findByid"/>
7 </resultMap>
8 <select id="findAll" resultMap="equipmentMap">
9    select * from equipment
10 </select>

第6行的 select 語句 呼叫的是 TypeMapper 下的findByid方法。

簡單來說, 我們呼叫了一個sql語句, 把type_id 查詢成 type物件

  <select id="findById" resultMap="typeResultMap">
        select *
        from type
        where id=#{id}
    </select>

不過,巢狀查詢會帶來 n+1 問題。 而巢狀結果不會, 所以推薦 第一種。

什麼是 n+1 問題

N+1問題就是這樣產生的:查詢主表是一次,查詢出N 條記錄,根據這N 條記錄,查詢關聯的副(從)表,共需要N 次。所以,應該叫1+N 問題更合適一些。

實際上, 在hibernate 中也預設有這種問題。

@ManyToOne標註的fetch的預設值是fetchType.EAGER

這時,hibernate除了發出查詢多的一方對應的表的記錄的sql語句外,還會發出n(多方記錄數)條sql語句,這就是1+n問題。

既然在hibernate中有這種問題。 根據目前的使用情況來看, 小型專案沒有必要管這個問題。

成功執行

image.png

collection 一對多

同樣的有兩種辦法

第一種方法:用select,跟association 中使用select類似:

public interface MobilePhoneDao {
    List<MobilePhone> queryMbByUserId(int userId);
}

實現該方法


<select id="queryMbByUserId" parameterType="int" resultMap="mobilePhoneMap">
        SELECT brand,price
        FROM tb_mobile_phone
        WHERE user_id=#{userId}
    </select>
1 <resultMap type="User" id="userMap">
2        <id property="userId" column="user_id"/>
3        <result property="userName" column="user_name"/>
4    <collection property="mobilePhone" column="user_id" 
                select="com.zhu.ssm.dao.MobilePhoneDao.queryMbByUserId">
5        </collection>
6 </resultMap> 

2、第二種方法:巢狀resultMap

<resultMap type="MobilePhone" id="mobilephoneMap">
         <id column="mobile_phone_id" property="mobilePhoneId"/>
         <result column="brand" property="brand" />
         <result column="price" property="price" /></resultMap>
    <resultMap type="User" id="userMap">
        <result property="userName" column="user_name"/>
        <result property="age" column="age"/>
        <collection property="mobilePhone" resultMap="mobilephoneMap" >
        </collection>
</resultMap> 

和association都類似。

相關文章