【MyBatis】的高級關係映射和查詢緩存詳解

1.高級結果映射(一對一、一對多、多對多)

  • 一對一映射
1)resultType
/**
 *訂單信息實體類
 * @see
 * @since
 */
public class Orders { 
    private Integer id;
    private Integer user_id;    //下單用戶id
    private String number;      //訂單號
    private Date createtime;    //創建訂單時間 
    private String note;        //備註     
    private String userName;    //用戶名
    private String sex;         //性別

    private User user;          //下單用戶  
    private List<OrderDetail> orderDetailList;  //訂單明細
    private List<Items> itemsList;  //商品
    ....
    省略getXxx()和setXxx()
}

public class User {
    private Integer id;
    private String userName;    //用戶名
    private String sex;         //性別
    private String birthday;    //生日
    private String address;     //地址    
    private List<Items> itemsList;  //商品
    private List<Orders> orders;    //訂單信息
    ....
    省略getXxx()和setXxx()
}

映射文件:
<select id="findOrdersAndUser" resultType="com.mybatis.assess.pojo.Orders">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex` 
    FROM
      orders,
      USER 
    WHERE orders.`user_id` = user.`id`;
</select>

Mapper接口:
public interface OrdersMapper {
    //一對一之resultType
    List<Orders> findOrdersAndUser() throws Exception;
}

測試代碼:
/**
 * 查詢訂單信息,關聯查詢用戶信息
 * @throws Exception
 */
@Test
public void findOrdersAndUser() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> list = mapper.findOrdersAndUser();
    System.out.println(list.toString());
    //關閉資源
    sqlSession.close();
}

小結:使用resultType來進行一對一結果映射,查詢出的列的個數和映射的屬性的個數要一致。而且映射的屬性要存在與一個大的對象中,它是一種平鋪式的映射,即數據庫查詢出多少條記錄,則映射成多少個對象。

2)resultMap
使用resultMap來進行一對一結果映射,它是將關聯對象添加到主信息的對象中,具體說是對象嵌套對象的一種映射方式。

映射文件:
<!-- id:用於標識resultMap,type值爲resultMap的結果類型 -->
<resultMap type="Orders" id="OrdersMap">
    <!-- 基本數據類型映射,column:數據庫的列,property:該列所對應的java屬性 -->
    <id column="id" property="id"/>
    <result column="user_id" property="user_id"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>

    <!-- 1)配置關聯對象: 
            association:映射一對一關聯關係
            property:當前Orsders對象的關聯對象名
            javaType:關聯對象屬性所屬的完全限定類名或別名
            select:所請求的select映射的ID,該select使用子查詢獲取其所關聯的屬性對象
            column:傳給子查詢的字段,即外鍵列
    -->     
        <association property="user" javaType="com.mybatis.assess.pojo.User" column="user_id">
            <result column="userName" property="userName"/>
            <result column="sex" property="sex"/>
        </association>      
</resultMap>

<select id="findOrdersAndUserRstMap" resultMap="OrdersMap">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex` 
    FROM
      orders,
      USER 
    WHERE orders.`user_id` = user.`id`;
</select>

測試代碼:
@Test
public void findOrdersAndUserRstMap() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();   
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> list = mapper.findOrdersAndUser(); 
    System.out.println(list.toString());    
    //關閉資源
    sqlSession.close();
}

小結:在一對一結果映射時,使用resultType更加簡單方便,如果有特殊要求(對象嵌套對象)時,需要使用resultMap進行映射,比如:查詢訂單列表,然後在點擊列表中的查看訂單明細按鈕,這個時候就需要使用resultMap進行結果映射。而resultType更適應於查詢明細信息,比如,查詢訂單明細列表。

  • 一對多映射
映射文件:
<resultMap type="Orders" id="OrdersMap">
    <!-- 基本數據類型映射,column:數據庫的列,property:該列所對應的java屬性 -->
    <id column="id" property="id"/>
    <result column="user_id" property="user_id"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>
    <result column="note" property="note"/>

    <!-- 1)配置關聯對象: 
            association:映射一對一關聯關係
            property:當前Orsders對象的關聯對象名
            javaType:關聯對象屬性所屬的完全限定類名或別名
            select:所請求的select映射的ID,該select使用子查詢獲取其所關聯的屬性對象
            column:傳給子查詢的字段,即外鍵列
    -->     
    <association property="user" javaType="com.mybatis.assess.pojo.User" column="user_id">
        <result column="userName" property="userName"/>
        <result column="sex" property="sex"/>
    </association>

    <!-- 2)配置關聯對象:
            collection:映射一對多關聯關係
            property:集合屬性的值
            ofType:集合中元素的類型
     -->
    <collection property="orderDetailList" ofType="com.mybatis.assess.pojo.OrderDetail">
        <id column="id" property="id"/>
        <result column="items_id" property="items_id"/>
        <result column="orders_id" property="orders_id"/>
        <result column="items_num" property="items_num"/>
    </collection>
</resultMap>

<select id="findOrdersAndOrderDetailRstMap" resultMap="OrdersMap">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex`,
      orderdetail.`id` detailId,
      orderdetail.`items_id`,
      orderdetail.`items_num` 
    FROM
      orders,
      USER,
      orderdetail 
    WHERE orders.`user_id` = user.`id` 
      AND orders.`id` = orderdetail.`orders_id`;
</select>

測試代碼:
/**
 * 查詢訂單信息,關聯查詢訂單詳情
 * @throws Exception
 */
@Test
public void findOrdersAndOrderDetailRstMap() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();   
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> orders = mapper.findOrdersAndOrderDetailRstMap();  
    for(Orders order : orders){
        System.out.println(order.getOrderDetailList().toString());
        System.out.println(order.getUser());
    }    
    //關閉資源
    sqlSession.close();
}
  • 多對多映射
    多對多映射是一對多映射的特例
/**
 * 訂單明細實體類
 * @see
 * @since
 */
public class OrderDetail {
    private Integer id;
    private Integer orders_id;  //訂單id
    private Integer items_id;   //商品id
    private Integer items_num;  //商品購買數量
    private Orders orders;      //訂單    
    private Items items;    //商品信息
    .....
    //省略getXxx()和setXxx()
}   
/**
 * 商品信息實體類
 * @see
 * @since
 */
public class Items {    
    private Integer id;
    private String name;        //商品名稱
    private Float price;        //商品定價
    private String detail;      //商品描述
    private String pic;         //商品圖片
    private Date createtime;    //生產日期  
    private List<Orders> OrdersList; //訂單
    .....
    //省略getXxx()和setXxx()
}   

Mapper接口:
public interface OrdersMapper {
    List<User> findUserAndItemsRstMap() throws Exception;   
}

映射文件:
<resultMap type="com.mybatis.assess.pojo.User" id="UserAndItemsRstMap">
    <!-- 用戶信息 -->
    <id column="user_id" property="id"/>
    <result column="userName" property="userName"/>
    <result column="sex" property="sex"/>
    <!-- 訂單信息(一對多) -->
    <collection property="orders" ofType="com.mybatis.assess.pojo.Orders">
        <id column="orders_id" property="id"/>
        <result column="user_id" property="user_id"/>
        <result column="number" property="number"/>
        <!-- 訂單明細信息(一對多) -->
        <collection property="orderDetailList" ofType="com.mybatis.assess.pojo.OrderDetail">
            <id column="id" property="id"/>
            <result column="items_id" property="items_id"/>
            <result column="orders_id" property="orders_id"/>
            <result column="items_num" property="items_num"/>
            <!-- 商品信息(一對一) -->
            <collection property="items" javaType="com.mybatis.assess.pojo.Items" column="items_id">
                <result column="name" property="name"/>
                <result column="price" property="price"/>
            </collection>   
        </collection>
    </collection>
</resultMap>

<select id="findUserAndItemsRstMap" resultMap="OrdersMap">
    SELECT 
      orders.`id`,
      orders.`user_id`,
      orders.`number`,
      user.`username`,
      user.`sex`,
      orderdetail.`id` detailId,
      orderdetail.`items_id`,
      orderdetail.`items_num`,
      items.`name`,
      items.`price` 
    FROM
      orders,
      USER,
      orderdetail,
      items 
    WHERE orders.`user_id` = user.`id` 
      AND orders.`id` = orderdetail.`orders_id` 
      AND orderdetail.`items_id` = items.`id`;
</select>

測試代碼:
/**
 * 查詢用戶信息,關聯查詢該用戶購買的商品信息
 * @throws Exception
 */
@Test
public void findUserAndItemsRstMap() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
    List<User> list = mapper.findUserAndItemsRstMap();
    //關閉資源
    sqlSession.close();
}

2.延遲加載

  • 2.1什麼是延遲加載
    延遲加載又叫懶加載,也叫按需加載。也就是說先加載主信息,在需要的時候,再去加載從信息。在mybatis中,resultMap標籤 的association標籤和collection標籤具有延遲加載的功能。
OrdersMapper映射文件:
<resultMap type="Orders" id="lazyLoadingRstMap">
    <id column="id" property="id"/>
    <result column="user_id" property="user_id"/>
    <result column="number" property="number"/>
    <result column="createtime" property="createtime"/>

    <association property="user" select="com.mybatis.assess.dao.IUserDao.queryUserById" column="user_id">
        <result column="userName" property="userName"/>
        <result column="sex" property="sex"/>
    </association>
</resultMap>

<!-- 延遲加載 -->
<select id="findOrderAndUserLazyLoading" resultMap="lazyLoadingRstMap">
    select * from Orders;
</select>

UserMapper映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper    
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"    
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.assess.dao.UserMapper">  
    <select id="queryUserById" parameterType="int" resultType="User">
        select * from user where id = #{id}
    </select>
</mapper>

測試代碼:
@Test
public void findOrderAndUserLazyLoading() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();   
    OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class); 
    List<Orders> list = mapper.findOrderAndUserLazyLoading();   
    for (Orders orders : list) {
        //在需要的時候,再去加載從信息(數據庫)
        System.out.println(orders.getUser());
    }   
    //關閉資源
    sqlSession.close();
}
  • 2,2設置延遲加載
<settings>
    <!--開啓延遲加載,默認值爲true-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--設置積極的懶加載,false的話按需加載,默認true-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings> 

3.查詢緩存

  • 3.1Mybatis的緩存理解
    這裏寫圖片描述
  • 3.2一級緩存
    這裏寫圖片描述
測試代碼:
@Test
public void oneLevelCatch() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    System.out.println("========================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    //關閉資源
    sqlSession.close();
}

這裏寫圖片描述

@Test
public void oneLevelCatchTest() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    IUserDao mapper = sqlSession.getMapper(IUserDao.class);
    User user1 = mapper.queryUserById(1);
    System.out.println(user1);

    //執行commit,將一級緩存清空
//      sqlSession.commit();
    sqlSession.clearCache();

    System.out.println("========================");
    User user2 = mapper.queryUserById(1);
    System.out.println(user2);
    //關閉資源
    sqlSession.close();
}

這裏寫圖片描述

  • 3.3二級緩存
    這裏寫圖片描述
1.開啓二級緩存
<settings>
    <!-- 二級緩存總開關  默認是false -->
    <setting name="cacheEnabled" value="true"/>
</settings> 
2.在mapper映射文件中開啓二級緩存
    <!-- 開啓二級緩存,默認使用了PerpetualCache -->
    <cache/>
3.序列化

這裏寫圖片描述

@Test
public void twoLevelCatch() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    SqlSession sqlSession2 = getSqlSessionFactory().openSession();
    SqlSession sqlSession3 = getSqlSessionFactory().openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);

    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    //在colse的時候,纔會將數據寫入二級緩存中
    sqlSession.close();
    System.out.println("========================");

    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);
    //在colse的時候,纔會將數據寫入二級緩存中
    sqlSession2.close();
    System.out.println("========================");

    sqlSession3.close();
}

這裏寫圖片描述

@Test
public void twoLevelCatchTest() throws Exception{
    SqlSession sqlSession = getSqlSessionFactory().openSession();
    SqlSession sqlSession2 = getSqlSessionFactory().openSession();
    SqlSession sqlSession3 = getSqlSessionFactory().openSession();

    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
    UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);

    User user1 = mapper.queryUserById(1);
    System.out.println(user1);
    //在colse的時候,纔會將數據寫入二級緩存中
    sqlSession.close();
    System.out.println("========================");

    //執行commit,將一級緩存清空
//      sqlSession3.commit();
    sqlSession.clearCache();
    System.out.println("========================");

    User user2 = mapper2.queryUserById(1);
    System.out.println(user2);
    //在colse的時候,纔會將數據寫入二級緩存中
    sqlSession2.close();
    System.out.println("========================");

    sqlSession3.close();
}

這裏寫圖片描述

  • 3.4禁用緩存
    這裏寫圖片描述
  • 3.5刷新緩存
    這裏寫圖片描述
  • 3.6整合ehcache
    Mybatis本身是一個持久層框架,它不是專門的緩存框架,所以它對緩存的實現不夠好,不能支持分佈式。Ehcache是一個分佈式的緩存框架。
1.什麼是分佈式
系統爲了提高性能,通常會對系統採用分佈式部署(集羣部署方式)

這裏寫圖片描述

2.整合思路
Cache是一個接口,它的默認實現是mybatis的PerpetualCache。如果想整合mybatis的二級緩存,那麼實現Cache接口即可。
2.1添加jar
ehcache-core-2.6.5.jar
mybatis-ehcache-1.0.2.jar
2.2設置映射文件中cache標籤的type值爲ehcache的實現類

這裏寫圖片描述

3.添加ehcache的配置文件
在config下,創建ehcache.xml

這裏寫圖片描述

4.測試ehcache的二級緩存

這裏寫圖片描述

  • 3.7應用場景
    使用場景:對於訪問響應速度要求高,但是實時性不高的查詢,可以採用二級緩存技術。
    注意:在使用二級緩存的時候,要設置一下刷新間隔(cache標籤中有一個flashInterval屬性)來定時刷新二級緩存,這個刷新間隔根據具體需求來設置,比如設置30分鐘、60分鐘等,單位爲毫秒。
  • 3.8侷限性
    Mybatis二級緩存對細粒度的數據,緩存實現不好。
    場景:對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次查詢都是最新的商品信息,此時如果使用二級緩存,就無法實現當一個商品發生變化只刷新該商品的緩存信息而不刷新其他商品緩存信息,因爲二級緩存是mapper級別的,當一個商品的信息發送更新,所有的商品信息緩存數據都會清空。
    解決此類問題,需要在業務層根據需要對數據有針對性的緩存。
    比如可以對經常變化的 數據操作單獨放到另一個namespace的mapper中。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章