開源頂級持久層框架——mybatis(ibatis)——day02
mybatis第二天 高階對映 查詢快取 和spring整合
課程複習:
mybatis是什麼?
mybatis是一個持久層框架,mybatis是一個不完全的ORM框架。SQL語句需要程式設計師自己去編寫,但是mybatis也有對映(輸入引數對映、輸出結果對映)。
mybatis入門門檻不高,學習成本低,讓程式設計師把精力放在sql語句上,對sql語句優化非常方便,適用於需求變化較多的專案,比如網際網路專案。
mybatis框架執行過程:
1.配置mybatis的配置檔案,SqlMapConfig.xml(名稱不固定)
2.通過配置檔案,載入mybatis執行環境,建立SqlSessionFactory會話工廠。
SqlSessionFactory在實際使用時按單例方式。
3.通過SqlSessionFactory建立SqlSession
SsqSession是一個面向使用者介面(提供操作資料庫方法),實現物件是執行緒不安全的,建議sqlSession應用場合在方法體內。
4.呼叫sqlSession的方法去操作資料。
如果需要提交事務,需要執行SqlSession的commit()方法。
5.釋放資源,關閉SqlSession
mybatis開發dao的方法:
1.原始dao的方法
需要程式設計師編寫dao介面和實現類。
需要在dao實現類中注入一個SqlSessionFactory工廠。
2.mapper代理開發方法(建議使用)
只需要程式設計師編寫mapper介面(就是dao介面)
程式設計師在編寫mapper.xml(對映檔案)和mapper.java的類全路徑。
1.mapper.xml中namespase就是mapper.java的類全路徑。
2.mapper.xml中statment的id和mapper.java中方法名一致。
3.mapper.xml中statment的resultType指定輸入型別和mapper.java的方法輸入引數型別一致。
4.mapper.xml中statment的resultType指定輸出結果的型別和mapper.java的方法返回值型別一致。
SqlMapConfig.xml配置檔案:可以配置properties屬性、別名、mapper載入....
輸入對映:
parameterType:指定輸入引數型別可以是簡單型別、pojo、hashMap...
對於綜合查詢,建議parameterType使用包裝的pojo,有利於系統擴充套件。
輸出對映:
resultType:
查詢到的列名和resultType指定的pojo的屬性名一致,才能對映成功。
resutlMap:
可以通過resultMap完成一些高階對映。
如果查詢到的列名和對映的pojo的屬性名不一致時,通過result Map設定列名和屬性名之間的對應關係(對映關係)。可以完成對映。
高階對映:
將關聯查詢的列對映到一個pojo屬性中。(一對一)
將關聯查詢的列對映到一個List<pojo>中。(一對多)
動態sql:(重點)
if判斷(掌握)
where
foreach
sql片段(掌握)
1.訂單商品資料模型
1.1資料模型分析思路
1.每張表記錄的資料內容
分模組對每張表記錄的內容進行熟悉,相當於你學習系統需求(功能)的過程。
2.每張表重要的欄位設定
非空欄位、外來鍵欄位
3.資料庫級別表與表之間的關係
外來鍵關係
4.表與表之間的業務關係
在分析表與表之間的業務關係時一定要建立在某個業務意義基礎上去分析。
1.2資料模型分析
使用者表:user:
記錄了購買商品的使用者資訊
訂單表:orders
記錄了使用者所建立的訂單(購買商品的訂單)
訂單明細表:orderdetail
記錄了訂單的詳細資訊即購買商品的資訊
商品表:items
記錄了商品資訊
表與表之間的業務關係:
在分析表與表之間的業務關係時需要建立在某個業務意義基礎上去分析。
先分析資料級別之間有關係的表之間的業務關係:
user和orders:
user--->orders:一個使用者可以建立多個訂單,一對多
orders--->user:一個訂單隻由一個使用者建立,一對多
orders和orderdatail:
orders--->orderdetail:一個訂單可以包括多個訂單明細,因為一個訂單可以購買多個商品,每個商品的購買資訊在orderdetail記錄,一對多關係
orderdetail--->orders:一個訂單明細只能包括在一個訂單中,一對一
orderdetail和items:
orderdetail--->items:一個訂單明細只對應一個商品資訊,一對一
items--->orderdetail:一個商品可以包括在多個訂單明細,一對多
再分析資料庫級別沒有關係的表之間是否有業務關係:
orders和items:
orders和items之間可以通過orderdetail表建立關係。
2.一對一查詢:
2.1需求:
查詢訂單資訊,在關聯查詢建立訂單的使用者資訊
2.2resultType
2.2.1sql語句
確定查詢的主表:訂單表
確定查詢的關聯表:使用者表
關聯表使用內連線?還是外連線
由於orders表中有一個外來鍵(user_id),通過外來鍵關聯查詢使用者表只能查詢出一個條記錄,可以使用內連線。
SELECT
orders.*,
user.username,
user.sex,
user.address
FROM
orders
INNER JOIN
user
ON
orders.user_id = user.id
2.2.2建立pojo
將上邊sql查詢的結果對映到pojo中,pojo中必須包括所有查詢列名。
原始的Orders.java不能對映全部欄位,需要新建立的pojo。
建立一個pojo繼承包括查詢欄位較多的po類。
//通過此類對映訂單和使用者查詢的結果,讓此類繼承包括欄位較多的pojo類
public class OrdersCustom extends Orders {
/*
* 新增使用者屬性
*/
private String username;
private String sex;
private String address;
2.2.3mapper.xml
<!-- 查詢訂單關聯查詢使用者資訊 -->
<select id="findOrdersUser"
resultType="com.changemax.mybatis.po.OrdersCustom">
SELECT orders.*, user.username, user.sex, user.address FROM
orders, user WHERE orders.user_id = user.id
</select>
2.2.4mapper.java
public List<OrdersCustom> findOrdersUser() throws Exception;
2.3resultMap
2.3.1sql語句
同resultType實現的sql
2.3.2使用resultMap對映的思路
使用resultMap將查詢結果中的訂單資訊對映到Orders物件中,在orders類中新增User屬性,將關聯查詢出來的使用者資訊對映到orders物件中的user屬性中。
2.3.3需要Orders類中新增user屬性
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
// 使用者資訊
private User user;
2.3.4mapper.xml
2.3.4.1定義resultMap
<!-- 訂單查詢關聯查詢使用者的resultMap -->
<resultMap type="com.changemax.mybatis.po.Orders"
id="OrdersUserResultMap">
<!-- 配置對映的訂單資訊 -->
<!-- id:指定查詢列中的唯一標識,訂單資訊的中的唯一標識,如果有多個列組成唯一標識,則需要配置多個id column:訂單資訊唯一標識列
property:訂單資訊的唯一標識列所對映到Orders中的哪個屬性 -->
<id column="id" property="id" />
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!-- 配置對映的關聯的使用者資訊 -->
<!-- association:用於對映關聯查詢單個物件的資訊 -->
<association property="user"
javaType="com.changemax.mybatis.po.User">
<!-- id:關聯查詢使用者的唯一標識 column:指定唯一標識使用者資訊的列 javaType:指定對映到user的哪個屬性 -->
<id column="id" property="id" />
<result column="username" property="username" />
<result column="sex" property="sex" />
<result column="birthday" property="birthday" />
<result column="address" property="address" />
</association>
</resultMap>
2.3.4.2statement定義
<!-- 查詢訂單關聯查詢使用者資訊,使用resultMap -->
<select id="findOrdersUserResultMap"
resultMap="OrdersUserResultMap">
SELECT orders.*, user.username, user.sex, user.address FROM
orders, user WHERE orders.user_id = user.id
</select>
2.3.5
public List<Orders> findOrdersUserResultMap() throws Exception;
2.4resultType和resultMap實現一對一查詢小結
實現一對一查詢:
resultType:使用resultType實現較為簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成對映。
如果沒有查詢結果的特殊要求建議使用resultType。
resultMap:需要單獨定義resultMap,實現有點麻煩,如果對查詢結果有特殊的要求,使用resutlMap可以完成將關聯查詢對映pojo的屬性中。
resutlMap可以實現延遲載入,resultType無法實現延遲載入。
3.一對多查詢
3.1需求
查詢訂單及訂單明細的資訊。
3.2sql語句
確定主查詢表:訂單表
確定關聯查詢表:訂單明細表
在一對一查詢基礎上新增訂單明細表關聯即可。
SELECT
orders.*,
USER.username,
USER.sex,
USER.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id
FROM
orders,
USER,
orderdetail
WHERE orders.user_id = user.id AND orderdetail.orders_id=orders.id
3.3分析
使用resultType將上邊的查詢結果對映到pojo中,訂單資訊的就是重複。
要求:
對orders對映不能出現重複記錄。
在orders.java類中新增List<OrderDetail> orderDetailList屬性。
最終會將訂單資訊對映到orders中,訂單所對應的訂單明細對映到orders中的orderDetailList屬性中。
對映成的orders記錄數為兩條(orders資訊不重複)
每個orders中的orderDetail屬性儲存了該訂單所對應的訂單明細。
3.4在orders中新增list訂單明細屬性
public class Orders {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
// 使用者資訊
private User user;
// 訂單明細
private List<Orderdetail> orderdetailList;
3.5mapper.xml
<!-- 查詢訂單關聯查詢使用者及訂單明細資訊,使用resultMap -->
<select id="findOrdersAndOrderDetailResultMap"
resultMap="OrdersAndOrderDetailResultMap">
SELECT
orders.*,
user.username,
user.sex,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id
FROM
orders,
user,
orderdetail
WHERE
orders.user_id = user.id
AND
orderdetail.orders_id =
orders.id
</select>
3.6resultMap定義
<!-- 查詢訂單及訂單明細的resultMap 使用extends繼承,就可以不用再在配置訂單資訊和使用者資訊的對映 -->
<resultMap type="com.changemax.mybatis.po.Orders"
id="OrdersAndOrderDetailResultMap" extends="OrdersUserResultMap">
<!-- 訂單資訊 -->
<!-- 使用者資訊 -->
<!-- 使用繼承,就可不需要再次配置訂單資訊和使用者資訊的對映關係 -->
<!-- 訂單明細 一個訂單查詢出了多條明細,要使用collection來進行對映 collection:對關聯查詢到多條記錄對映到com.changemax.mybatis.po.Orders哪個屬性
ofType:對映到集合屬性的pojo的型別 -->
<collection property="orderdetailList"
ofType="com.changemax.mybatis.po.Orderdetail">
<!-- id:訂單明細的唯一標識 property:要將訂單明細的唯一標識對映到com.changemax.mybatis.po.Orderdetail的哪個屬性 -->
<id column="orderdetail_id" property="id" />
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum" />
<result column="orders_id" property="ordersId" />
</collection>
</resultMap>
3.7mapper.java
//查詢訂單及訂單明細
public List<Orders> findOrdersAndOrderDetailResultMap() throws Exception;
3.8小結
mybatis使用resultMap的collection對關聯查詢的多條記錄對映到一個list集合屬性中。
使用resultType實現:
將訂單明細對映到orders的orderdetailList中,需要自己處理,使用雙重迴圈遍歷,去掉重複記錄,將訂單明細放在orderdetailList中。
4.多對多查詢
4.1需求
查詢使用者及使用者購買商品資訊。
4.2sql語句
查詢主表的是:使用者表
關聯表:由於使用者和商品沒有直接關聯,通過訂單和訂單明細進行關聯,所以關聯表:orders、orderdetail、items
SELECT
orders.*,
user.username,
user.sex,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.name items_name,
items.detail items_detail,
items.price items_price
FROM
orders,
user,
orderdetail,
items
WHERE
orders.user_id = user.id
AND
orderdetail.orders_id =
orders.id
AND
orderdetail.items_id =
items.id
4.3對映思路
將使用者資訊對映到user中
在User類中新增訂單列表屬性List<Orders> ordersList,將使用者建立的訂單對映到ordersList
在Orders中新增訂單明細列表屬性List<OrderDetail> orderdetailList,
在OrderDetail中新增Items屬性,將訂單明細所對應的商品對映到Items
4.4mapper.xml
<!-- 查詢使用者及購買的商品資訊,使用resultMap -->
<select id="findUserAndItemsResultMap"
resultMap="UserAndItemsResultMap">
SELECT
orders.*,
user.username,
user.sex,
user.address,
orderdetail.id orderdetail_id,
orderdetail.items_id,
orderdetail.items_num,
orderdetail.orders_id,
items.name items_name,
items.detail items_detail,
items.price items_price
FROM
orders,
user,
orderdetail,
items
WHERE
orders.user_id = user.id
AND
orderdetail.orders_id =
orders.id
AND
orderdetail.items_id =
items.id
</select>
4.5resultMap定義
<!-- 查詢使用者及購買的商品資訊對映 -->
<resultMap type="com.changemax.mybatis.po.User" id="UserAndItemsResultMap">
<!-- 使用者資訊 -->
<id column="user_id" property="id"/>
<id column="username" property="username"/>
<id column="sex" property="sex"/>
<id column="address" property="address"/>
<!-- 訂單資訊
一個使用者對應對個訂單,使用collection對映-->
<collection property="ordersList" ofType="com.changemax.mybatis.po.Orders">
<id column="id" property="id"/>
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!-- 訂單明細
一個訂單包括多個明細
-->
<collection property="orderdetailList" ofType="com.changemax.mybatis.po.Orderdetail">
<id column="orderdetail_id" property="id" />
<result column="items_id" property="itemsId" />
<result column="items_num" property="itemsNum" />
<result column="orders_id" property="ordersId" />
<!-- 商品資訊
一個訂單對應一個商品新 -->
<association property="items" javaType="com.changemax.mybatis.po.Items">
<id column="items_id" property="id"/>
<result column="items_name" property="name"/>
<result column="items_price" property="price"/>
<!-- <result column="pic" property="pic"/>
<result column="createtime" property="createtime"/> -->
<result column="items_detail" property="detail"/>
</association>
</collection>
</collection>
</resultMap>
4.6mapper.java
//查詢使用者及商品的購買資訊
public List<User> findUserAndItemsResultMap() throws Exception;
4.7多對多查詢總結
將查詢使用者購買的商品資訊明細清單,(使用者名稱、使用者地址、購買商品名稱、購買商品時間、購買商品數量)
針對上邊的需求就使用resultType將查詢到的記錄對映到一個擴充套件的pojo中,很簡單實現明細清單的功能。
一對多是多對多的特例,如下需求:
查詢使用者購買的商品資訊,使用者和商品的關係是多對多關係。
需求1:
查詢欄位:使用者賬號、使用者名稱稱、使用者性別、商品名稱、商品價格(最常見)
企業開發中常見明細列表,使用者購買商品明細列表,
使用resultType將上邊查詢列對映到pojo輸出。
需求2:
查詢欄位:使用者賬號、使用者名稱稱、購買商品數量、商品明細(滑鼠移上即可顯示明細)
使用resultMap將使用者購買的商品明細列表對映到user物件中。
總結:
使用resultMap是針對那些對查詢結果對映有特殊要求的功能,比如特殊要求對映成list中包括多個list。
5.resultMap總結
resultType
作用:
將查詢結果按照sql列名pojo屬性名一致性對映到pojo中。
場合:
常見一些明細記錄的展示,比如使用者購買商品明細,將關聯查詢資訊全部展示在頁面時,此時可以直接使用result將每一條記錄對映到pojo中,在前端頁面遍歷list(list中是pojo)即可。
resultMap:
使用association和collection完成一對一和一對多高階對映(對結果有特殊的對映要求)。
association:
作用:
將關聯查詢資訊對映到一個pojo物件中。
場合:
為了方便查詢關聯資訊可以使用association將關聯訂單資訊對映為使用者物件的pojo屬性中,比如:查詢訂單及關聯使用者資訊。
使用resultType無法將查詢結果對映到pojo物件的pojo屬性中,根據對結果集查詢遍歷的需要選擇使用resultType還是resultMap。
collection:
作用:
將關聯查詢資訊對映到一個list集合中。
場合:
為了方便查詢遍歷關聯資訊可以使用collection將關聯資訊對映到list集合中,比如:查詢使用者許可權範圍模組及模組下的選單,可以使用collection將模組對映到模組list中,將選單列表對映到模組物件的選單list屬性中,這樣的做的目的也是方便對查詢結果集進行遍歷查詢。
如果使用resultType無法將查詢結果對映到list集合中。
6.延遲載入
6.1什麼是延遲載入
resultMap可以實現高階對映(使用association、collection實現一對一及一對多對映),association、collection具備延遲載入功能。
需求:
如果查詢訂單並且關聯查詢使用者資訊。如果先查詢訂單資訊即可滿足要求,當我們需要查詢使用者資訊時再查詢使用者資訊。把對使用者資訊的按需去查詢就是延遲載入。
延遲載入:先從單表查詢、需要時再從關聯表去關聯查詢,大大提供資料庫效能,因為查詢單表要比關聯查詢多張錶速度要快。
6.2使用association實現延遲載入
6.2.1需求:
查詢訂單並且關聯查詢使用者資訊
6.2.2mapper.xml
需要定義兩個mapper的方法對應的statement。
1.只查詢訂單資訊
SELECT * FROM orders
在查詢訂單的statement中使用association去延遲載入(執行)下邊的satatement(關聯查詢使用者資訊)
<!-- 查詢訂單關聯查詢使用者,使用者資訊需要延遲載入 -->
<select id="findOrderUserLazyLoading" resultMap="orderUserLazyLoadingResutlMap">
SELECT * FROM orders
</select>
2.關聯查詢使用者資訊
通過上邊查詢到的訂單資訊中user_id去查詢使用者資訊
使用UserMapper.xml中的findUserById
<select id="findUserById" parameterType="int" resultType="user">
SELECT
* FROM USER WHERE id=#{value}
</select>
上邊先去執行findOrdersUserLazyLoading,當需要去查詢使用者的時候再去執行findUserById,通過resultMap的定義將延遲載入執行配置起來。
6.2.3延遲載入resultMap
使用association中的select指定延遲載入去執行的statement的id。
<!-- 延遲載入的resultMap -->
<resultMap type="com.changemax.mybatis.po.Orders" id="orderUserLazyLoadingResutlMap">
<!-- 對訂單資訊進行對映配置 -->
<id column="id" property="id"/>
<result column="user_id" property="userId" />
<result column="number" property="number" />
<result column="createtime" property="createtime" />
<result column="note" property="note" />
<!-- 實現使用者資訊進行延遲載入
select:指定延遲載入需要執行的statement的id(是根據user_id查詢使用者資訊的statement)
要使用userMapper.xml中findUserById完成根據使用者id(user_id)使用者資訊的查詢,如果findUserById不在本mapper中需要起那麼加namespace
column:訂單資訊中關聯使用者資訊查詢的列,是user_id
關聯查詢的sql查詢理解為:
SELECT
orders.*,
(SELECT username FROM user WHERE orders.user_id = user.id) username,
(SELECT sex FROM user WHERE orders.user_id = user.id) sex,
FROM
orders
-->
<association property="user" javaType="com.changemax.mybatis.po.User" select="com.changemax.mybatis.mapper.UserMapper.findUserById" column="user_id">
<!-- 使用者資訊 -->
</association>
</resultMap>
6.2.4mapper.java
//查詢訂單關聯查詢使用者,使用者資訊是延遲載入
public List<Orders> findOrdersUserLazyLoading()throws Exception;
6.2.5測試
6.2.5.1測試思路:
1.執行上邊mapper方法(findOrdersUserLazyLoading),內部去呼叫com.changemax.mybatis.mapper.OrderMapperCustom中的findOrdersUserLazyLoading只查詢orders資訊(單表)。
2.在程式中去遍歷上一步驟查詢出的List<Orders>,當我們呼叫Orders中的getUser()方法時,開始進行延遲載入。
3.延遲載入,去呼叫UserMapper.xml中findUserById這個方法獲取使用者資訊。
6.2.5.2延遲載入配置
mybatis預設沒有開啟延遲載入,需要在SqlMapConfig.xml中setting配置。
在mybatis核心配置檔案中配置:
lazyLoadingEnabled、aggressiveLazyLoading
設定項 描述 允許值 預設值
lazyLoadingEnabled 全域性性設定懶載入。如果設為‘false’,則所有相關聯的都會被初始化載入。 true | false false
aggressiveLazyLoading 當設定為‘true’的時候,懶載入的物件可能被任何懶屬性全部載入。否則,每個屬性都按需載入。 true | false true
在sqlMapConfig.xml中配置:
<!-- 全域性配置引數,需要時設定 -->
<settings>
<!-- 開啟延遲載入開關 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 關閉積極載入的開關 -->
<setting name="aggressiveLazyLoading" value="false"/>