mybatis實現物件之間的關係(一對一、一對多、多對多)以及mybatis的快取機制
一、業務需求中物件之間的關係
在實際的開發當中,不僅要對錶與表之間的關係進行的詳細分析,也要針對在業務意義上物件之間的關係,通常的關係為:一對一、一對多、多對多;
二、針對使用mybatis處理這三種關係
1、業務模型說明
這裡用用使用者表、訂單表、訂單詳情表、商品表著四張表之間,說明物件這三種之間的關係:
2、一對一的關係
2.1、用查詢訂單表的詳情orders表,來說明一對一的關係(一個訂單資訊對應一個使用者)
2.2、使用resultType和resultMap都可以實現一對一的關係的
2.21、使用resultType實現一對一的關係
resultType標籤對應的結果對映到一個包裝類上面,這個包裝類包含著order表和user表的所有的屬性資訊;(很好理解啊,就是sql查詢返回的資料要對應到java物件中,如果使用包裝型別的設計方法就是用resultTpye,如果使用在兩者中插入某一類的物件作為屬性,就是用resultMap)
包裝類:
對映檔案.xmlpublic class OrdersCustom extends Orders { //使用者名稱稱 private String username; //使用者地址 private String address; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
<!-- 使用resultType實現訂單查詢,關聯查詢使用者資訊 -->
<select id="findOrdersUserList" resultType="cn.itcast.mybatis.po.OrdersCustom">
SELECT
orders.*,
user.username,
user.address
FROM
orders,
USER
WHERE orders.user_id =
user.id
</select>
對用的mapper介面
// 查詢訂單及使用者資訊 public List<OrdersCustom> findOrdersUserList() throws Exception;
2.22、使用resultMap來說明一對一的關係
(這裡resultMap對映到orders物件後,但是裡面又有user要對映,所以,相當於在orders物件對映之後又對映到user一次,相當於連續兩次的對映,所以在是一對一的情況下,使用resultType)
對映到java物件
public class Orders {
private int id;//訂單id
private int user_id;//使用者id
private String order_number;//訂單號
//使用者資訊
private User user;
對映檔案.xml
<!-- 定義訂單資訊及使用者資訊的resultMap -->
<resultMap type="orders" id="ordersUserResultMap">
<!-- id:訂單資訊的唯 一約束 如果由多個欄位決定一條唯 一記錄,id標籤需要定義多個 -->
<id column="id" property="id" />
<result column="order_number" property="order_number" />
<result column="user_id" property="user_id" />
<!-- 配置使用者對映資訊 將sql查詢的使用者資訊對映到orders中的user屬性中 association:用於單個關聯物件的對映 property:將關聯資訊對映到orders的哪個屬性
javaType:對映屬性的型別 -->
<association property="user" javaType="cn.itcast.mybatis.po.User">
<!-- id:關聯的使用者資訊的唯 一約束 property:id指定 的列對映到關聯cn.itcast.mybatis.po.User類的哪個屬性 -->
<id column="user_id" property="id" />
<result column="username" property="username" />
<result column="address" property="address" />
</association>
</resultMap>
<!-- 使用resultMap實現訂單查詢,關聯查詢使用者資訊 -->
<select id="findOrdersUserListResultMap" resultMap="ordersUserResultMap">
SELECT
orders.*,
user.username,
user.address
FROM
orders,
USER
WHERE orders.user_id
= user.id
</select>
對用的mapper介面
// 查詢訂單及使用者資訊使用resultMap
public List<Orders> findOrdersUserListResultMap() throws Exception;
3、一對多的關係完成的需求:根據訂單orders查詢到訂單的明細orderdetail
resultMap將訂單明細資訊對映到Orders.java的屬性中;
(實際上就是在orders的物件當中把orderdetail集合又映射了一遍)
對映的java物件中;public class Orders {
private int id;//訂單id
private int user_id;//使用者id
private String order_number;//訂單號
//使用者資訊
private User user;
//訂單明細
private List<Orderdetail> orderdetails;
對映檔案.xml
注意,為了防止程式碼的可重用性,mybatis不止提供了sql片段,還提供了繼承:
<!-- 定義訂單及訂單明細的resultMap extends:繼承哪個resultMap,如果 是跨namespace前邊加namespace是 -->
<resultMap type="orders" id="ordersUserDetailResultMap"
extends="ordersUserResultMap">
<!-- 訂單及使用者資訊,繼承於ordersUserResultMap -->
<!-- 對映訂單明細資訊 collection:對映集合物件 property:將明細資訊對映到哪個集合屬性中。 ofType:集合中物件的型別 -->
<collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
<!-- id:訂單明細的唯 一約束 property:ofType指定 型別的屬性 -->
<id column="orderdetail_id" property="id" />
<result column="item_id" property="item_id" />
<result column="item_num" property="item_num" />
<result column="item_price" property="item_price" />
</collection>
<!-- 查詢訂單及訂單明細資訊 -->
<select id="findOrdersUserDetailList" resultMap="ordersUserDetailResultMap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.item_id,
orderdetail.item_num,
orderdetail.item_price
FROM
orders,
USER,
orderdetail
WHERE orders.user_id = user.id AND orders.id =
orderdetail.orders_id
</select>
對應的mapper介面:// 查詢訂單及訂單明細資訊
public List<Orders> findOrdersUserDetailList() throws Exception;
3、多對多關係
需求:查詢所有訂單資訊及訂單明細的商品資訊
對映到的java物件:
public class Orders {
private int id;//訂單id
private int user_id;//使用者id
private String order_number;//訂單號
//使用者資訊
private User user;
//訂單明細
private List<Orderdetail> orderdetails;
因為在 //訂單明細 private List<Orderdetail> orderdetails;中有外來鍵關聯的item資訊,所以這裡可以使用在Orderdetail物件中設定Items的屬性:public class Orderdetail {
private int id;//主鍵
private int orders_id;//訂單id
private int item_id;//商品id
private int item_num;//商品數量
private Float item_price;//商品價格
//商品資訊
private Items items;//明細對應的商品資訊
對應的mapper.xml
<!-- 定義訂單及明細和商品資訊 -->
<resultMap type="orders" id="ordersUserDetailItemResultMap"
extends="ordersUserResultMap">
<!-- 訂單及使用者資訊,繼承於ordersUserResultMap -->
<!-- 對映訂單明細資訊 collection:對映集合物件 property:將明細資訊對映到哪個集合屬性中。 ofType:集合中物件的型別 -->
<collection property="orderdetails" ofType="cn.itcast.mybatis.po.Orderdetail">
<!-- id:訂單明細的唯 一約束 property:ofType指定 型別的屬性 -->
<id column="orderdetail_id" property="id" />
<result column="item_id" property="item_id" />
<result column="item_num" property="item_num" />
<result column="item_price" property="item_price" />
<!-- 對映商品資訊 property:將商品資訊對映到cn.itcast.mybatis.po.Orderdetail的items屬性中 -->
<association property="items" javaType="cn.itcast.mybatis.po.Items">
<!-- id:商品資訊的唯 一標識 -->
<id column="item_id" property="id" />
<result column="item_detail" property="item_detail" />
<result column="item_name" property="item_name" />
<result column="item_price_price" property="item_price" />
</association>
</collection>
</resultMap>
<!-- 查詢訂單及訂單明細資訊及商品資訊 -->
<select id="findOrdersUserDetailItemList" resultMap="ordersUserDetailItemResultMap">
SELECT
orders.*,
user.username,
user.address,
orderdetail.id orderdetail_id,
orderdetail.item_id,
orderdetail.item_num,
orderdetail.item_price ,
items.item_detail,
items.item_name,
items.item_price item_price_price
FROM
orders,
USER,
orderdetail,
items
WHERE orders.user_id = user.id
AND
orders.id = orderdetail.orders_id
AND orderdetail.item_id = items.id
</select>
對應的mapper介面:
//查詢訂單及明細和商品資訊
public List<Orders> findOrdersUserDetailItemList()throws Exception;
三、mybatis的快取機制
將從資料庫中查詢出來的資料快取起來,快取介質:記憶體、磁碟,從快取中取資料,而不從資料庫查詢,減少了資料庫的操作,提高了資料處理效能。
1、mybatis的延遲載入
延遲載入意義:在需求允許的情況下,先查詢單表,當需要關聯其它表查詢時,進行延遲載入,去關聯查詢;
達到目標:不需要關聯資訊時不查詢,需要時再查詢。好處:提高資料庫的效能。
1.1、mybatis延遲載入的配置:
在SqlMapConfig.xml中配置setting全域性引數:
lazyLoadingEnabled:延遲載入的總開關,設定為true
aggressiveLazyLoading:設定為false,實現按需載入(將積極變為消極)
<!-- 全域性配置引數 -->
<settings>
<!-- 開啟延遲載入 -->
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="false" />
</settings>
在mapper.xml的配置檔案裡配置延遲載入:(
需求:
查詢訂單資訊,關聯查詢使用者資訊。
延遲載入需求:首次只查詢訂單資訊,當需要關聯查詢使用者資訊時,再查詢使用者資訊。
)<!-- 訂單及使用者的resultMap,實現延遲載入 -->
<resultMap type="orders" id="ordersResultMap">
<!-- 配置訂單資訊的對映 -->
<id column="id" property="id" />
<result column="user_id" property="user_id" />
<result column="order_number" property="order_number" />
<!-- 配置延遲載入 使用者資訊
select:延遲載入 時呼叫 的statement,如果跨名稱空間,需要加上namespace
column:將哪一列的值作為引數 傳到延遲載入 的statement -->
<association property="user" javaType="cn.itcast.mybatis.po.User"
select="cn.itcast.mybatis.mapper.UserMapper.findUserById" column="user_id">
</association>
</resultMap>
<!-- 訂單資訊查詢,使用延遲載入 -->
<select id="findOrdersList" resultMap="ordersResultMap">
select * from orders
</select>
2、mybatis的一級快取
Mybatis預設提供一級快取,快取範圍是一個sqlSession。(也就是指的一條sql語句)
在同一個SqlSession中,兩次執行相同的sql查詢,第二次不再從資料庫查詢。
執行提交清除快取測試:
如果第一次查詢後,執行commit提交,mybatis會清除快取,第二次查詢從資料庫查詢。
一級快取原理:
一級快取採用Hashmap儲存,mybatis執行查詢時,從快取中查詢,如果快取中沒有從資料庫查詢。
如果該SqlSession執行commit()提交,清除快取。
3、mybatis的二級快取
快取範圍是跨SqlSession的,範圍是mapper的namespace,相同的namespace使用一個二級快取結構。
需要進行引數配置讓mybatis支援二級快取
3.1、二級快取的配置在全域性的配置檔案sqlMapConfig.xml中配置:
<!-- 全域性配置引數 -->
<settings>
<!-- 開啟二級快取 -->
<setting name="cacheEnabled" value="true"/>
</settings>
在mapper.xml中的配置:
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 開啟二級快取 -->
<cache />
3.11、使用二級快取注意的事項
- l實現序列化:
注意:將查詢對映物件結果的pojo物件進行序列化實現 java.io.Serializable介面
- l 如何清除快取:
執行相同的statement,如果執行提交操作需要清除二級快取。
如果想讓statement執行後重新整理快取(清除快取),在statement中設定flushCache="true" (預設值是true)
- l 設定statement是否開啟二級快取
如果讓某個statement啟用二級快取,設定useCache=true(預設值為true)
3.2、二級快取原理
如果二快取開啟,首先從二級快取查詢資料,如果二級快取有則從二級快取中獲取資料,如果二級快取沒有,從一級快取找是否有快取資料,如果一級快取沒有,查詢資料庫。
4、使用Ehcache進行二級快取
Mybatis控制二級快取策略,二級快取快取介質使用Ehcache。
讓Ehcache和mybatis進行整合。
實際上還是mybatis進行控制Ehcache,把快取的資料儲存在記憶體當中!