mybatis實現對象之間的關係(一對一、一對多、多對多)以及mybatis的緩存機制

一、業務需求中對象之間的關係

在實際的開發當中,不僅要對錶與表之間的關係進行的詳細分析,也要針對在業務意義上對象之間的關係,通常的關係爲:一對一、一對多、多對多;

二、針對使用mybatis處理這三種關係

1、業務模型說明

這裏用用用戶表、訂單表、訂單詳情表、商品表着四張表之間,說明對象這三種之間的關係:


2、一對一的關係

2.1、用查詢訂單表的詳情orders表,來說明一對一的關係(一個訂單信息對應一個用戶)

2.2、使用resultType和resultMap都可以實現一對一的關係的

2.21、使用resultType實現一對一的關係

resultType標籤對應的結果映射到一個包裝類上面,這個包裝類包含着order表和user表的所有的屬性信息;(很好理解啊,就是sql查詢返回的數據要對應到java對象中,如果使用包裝類型的設計方法就是用resultTpye,如果使用在兩者中插入某一類的對象作爲屬性,就是用resultMap)

包裝類:

public 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;
	}
	
	
}
映射文件.xml

<!-- 使用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的,範圍是mappernamespace,相同的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

 

  • 設置statement是否開啓二級緩存

如果讓某個statement啓用二級緩存,設置useCache=true(默認值爲true


3.2、二級緩存原理

 

如果二緩存開啓,首先從二級緩存查詢數據,如果二級緩存有則從二級緩存中獲取數據,如果二級緩存沒有,從一級緩存找是否有緩存數據,如果一級緩存沒有,查詢數據庫。

 


4、使用Ehcache進行二級緩存

Mybatis控制二級緩存策略,二級緩存緩存介質使用Ehcache

Ehcachemybatis進行整合。


實際上還是mybatis進行控制Ehcache,把緩存的數據存儲在內存當中!

示例代碼:https://github.com/libolibolibo/mybatis1217_22.git


這裏附上mybatis的逆向工程(開發時可以使用該工程):https://github.com/libolibolibo/generatorSqlmapCustom



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章