記錄一下從hibernate 改成 mybatis 遇到的一些問題
小問題一
執行的時候突然發現的報錯:
檢查過幾遍之後才發現原因: 之前的時候不小心在後面增加了一個斜槓
由於編譯器不會對 sql 語句進行檢查, 所以執行後臺的時候並沒有報錯。所以當時沒發現。
而過幾天之後,當執行到這條sql 語句的時候, 後臺才報了錯。
所以,我們寫sql 語句的時候要小心。
以防我們寫的不對, 還可以進入mysql 執行一下這條語句看看結果。
二
發現問題
背景: 分頁的時候發現這三列資料為空
效果圖:
排查問題:
內部編號這一列的問題很快就排查了出來:
後臺與資料庫欄位名不一致
資料庫:
後臺實體:
所以在我們執行下面這條語句時,在進行實體對映的時候對應不上, 所以為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 屬性寫上資料庫名。
最後就對映成功。在列表中可以看到這列。
-
剩下兩列還為空。 原因是: 剩下兩個屬性都是物件
因為我們在資料庫中存的是 主鍵。
所以我們沒辦法使用, 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>
2. 巢狀查詢 select
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中有這種問題。 根據目前的使用情況來看, 小型專案沒有必要管這個問題。
成功執行
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都類似。