mybatis的一對一,一對多查詢,延遲加載,緩存介紹

一對一查詢
  需求
    查詢訂單信息關聯查詢用戶信息
  sql語句前端

/*經過orders關聯查詢用戶使用user_id一個外鍵,只能關聯查詢出一條用戶記錄就可使用內鏈接*/
SELECT orders.*,user.username,user.sex 
FROM orders,USER 
WHERE orders.user_id = user.id

  使用resultType實現
    一對一查詢映射的pojo
      建立pojo包括 訂單信息和用戶信息,resultType才能夠完成映射。建立OrderCustom做爲自定義pojo,繼承sql查詢列多的po類。java

public class OrderCustom extends Orders {
    //補充用戶信息
    private String username;
    private String sex;
    private String address;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "OrderCustom [username=" + username + ", sex=" + sex
                + ", address=" + address + "]";
    }
}
OrderCustom

    mapper.xmlspring

<!-- 一對一查詢使用reusltType完成    查詢訂單關聯查詢用戶信息 -->
<select id="findOrderUserList" resultType="orderCustom">
  SELECT orders.*,user.username,user.sex 
  FROM orders,USER 
  WHERE orders.user_id = user.id 
</select>

    mapper.javasql

public interface OrdersMapperCustom {
    // 一對一查詢,查詢訂單關聯查詢用戶,使用resultType
    public List<OrderCustom> findOrderUserList() throws Exception;
    // 一對一查詢,使用resultMap
    public List<Orders> findOrderUserListResultMap() throws Exception;
    //一對一查詢,延遲加載
    public List<Orders> findOrderUserListLazyLoading() throws Exception;
    // 一對多查詢,使用resultMap
    public List<Orders> findOrderAndOrderDetails() throws Exception;
    // 一對多查詢,使用resultMap
    public List<User> findUserOrderDetail() throws Exception;
}
OrdersMapperCustom

  使用resultMap實現一對一
    resultMap映射思路
      resultMap提供一對一關聯查詢的映射和一對多關聯查詢映射,一對一映射思路:將關聯查詢的信息映射到pojo中,以下:在Orders類中建立一個User屬性,將關聯查詢的信息映射到User屬性中。數據庫

public class Orders implements Serializable {
    private Integer id;
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;
    //關聯用戶信息
    private User user;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number == null ? null : number.trim();
    }
    public Date getCreatetime() {
        return createtime;
    }
    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }
    public User getUser() {
    return user;
    }
    public void setUser(User user) {
    this.user = user;
    }
}
Orders

    mapper.xmlapache

<!-- 一對一查詢使用reusltMap完成    查詢訂單關聯查詢用戶信息    -->
<select id="findOrderUserListResultMap" resultMap="ordersUserResultMap" >
  SELECT orders.*,user.username,user.sex 
  FROM orders,USER 
  WHERE orders.user_id = user.id 
</select>

    resultMap定義緩存

<resultMap type="orders" id="ordersUserResultMap">
        <!-- 完成了訂單信息的映射配置 -->
        <!-- id:訂單關聯用戶查詢的惟 一 標識  -->
        <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:用於對關聯信息映射到單個pojo
          property:要將關聯信息映射到orders的哪一個屬性中
          javaType:關聯信息映射到orders的屬性的類型,是user的類型
         -->
        <association property="user" javaType="user">
            <!-- id:關聯信息的惟 一標識  -->
            <!-- property: 要映射到user的哪一個屬性中-->
            <id column="user_id" property="id"/>
            <!-- result就是普通列的映射 -->
            <result column="username" property="username"/>
            <result column="sex" property="sex"/>
        </association>
</resultMap>

  小結
    resultType:要自定義pojo 保證sql查詢列和pojo的屬性對應,這種方法相對較簡單,因此應用普遍。
    resultMap:使用association完成一對一映射須要配置一個resultMap,過程有點複雜,若是要實現延遲加載就只能用resultMap實現 ,若是爲了方便對關聯信息進行解析,也能夠用association將關聯信息映射到pojo中方便解析。
一對多查詢
  需求
    查詢全部訂單信息及訂單下的訂單明細信息。
  sql語句session

SELECT orders.*,user.username,user.sex ,orderdetail.id orderdetail_id,orderdetail.items_num,orderdetail.items_id
FROM orders,USER,orderdetail
WHERE orders.user_id = user.id AND orders.id = orderdetail.orders_id

  resultMap進行一對多映射思路
    resultMap 提供collection完成關聯信息映射到集合對象中。在orders類中建立集合屬性數據結構

public class Orders implements Serializable {
    private Integer id;
    private Integer userId;
    private String number;
    private Date createtime;
    private String note;
    //關聯用戶信息
    private User user;
    //訂單明細
    private List<Orderdetail> orderdetails;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number == null ? null : number.trim();
    }
    public Date getCreatetime() {
        return createtime;
    }
    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note == null ? null : note.trim();
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public List<Orderdetail> getOrderdetails() {
        return orderdetails;
    }
    public void setOrderdetails(List<Orderdetail> orderdetails) {
        this.orderdetails = orderdetails;
    }  
}
Orders

  mapper.xmlmybatis

<!-- 一對多查詢使用reusltMap完成    查詢訂單關聯查詢訂單明細    -->
<select id="findOrderAndOrderDetails" resultMap="orderAndOrderDetails" >
  SELECT orders.*,user.username,user.sex ,orderdetail.id orderdetail_id,orderdetail.items_num,orderdetail.items_id
    FROM orders,USER,orderdetail
    WHERE orders.user_id = user.id  AND orders.id = orderdetail.orders_id
</select>

  resultMap定義

<!-- 一對多,查詢訂單及訂單明細 -->
<resultMap type="orders" id="orderAndOrderDetails" extends="ordersUserResultMap">
  <!-- 映射訂單信息,和用戶信息,這裏使用繼承ordersUserResultMap -->
  <collection property="orderdetails" ofType="cn.yzu.mybatis.po.Orderdetail">
    <!-- id:關聯信息訂單明細的惟 一標識
      property:Orderdetail的屬性名
    -->
    <id column="orderdetail_id" property="id"/>
      <result column="items_num" property="itemsNum"/>
      <result column="items_id" property="itemsId"/>
    </collection>
</resultMap>

一對多查詢(複雜)
  需求
    查詢全部用戶信息,關聯查詢訂單及訂單明細信息及商品信息,訂單明細信息中關聯查詢商品信息
  pojo定義
    在user.java中建立映射的屬性:集合 List<Orders> orderlist
    在Orders中建立映射的屬性:集合List<Orderdetail> orderdetails
    在Orderdetail中建立商品屬性:pojo Items items
  mapper.xml

<!-- 一對多查詢使用reusltMap完成 查詢用戶及訂單和訂單明細,關聯商品,的信息-->
<select id="findUserOrderDetail" resultMap="userOrderDetailResultMap" >
  SELECT orders.*,user.username,user.sex ,orderdetail.id orderdetail_id,orderdetail.items_num,orderdetail.items_id,items.name items_name,items.detail items_detail
  FROM orders,USER,orderdetail,items
  WHERE orders.user_id = user.id  AND orders.id = orderdetail.orders_id AND items.id = orderdetail.items_id
</select>

  resultMap

<!-- 一對多查詢,查詢用戶及訂單明細和商品信息 -->
<resultMap type="user" id="userOrderDetailResultMap">
  <!-- 用戶信息映射 -->
  <id column="user_id" property="id"/>
  <result column="username" property="username"/>
  <result column="sex" property="sex"/>
  <!-- 訂單信息 -->
  <collection property="orderlist" ofType="cn.yzu.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="orderdetails" ofType="cn.yzu.mybatis.po.Orderdetail">
      <!-- id:關聯信息訂單明細的惟 一標識
          property:Orderdetail的屬性名
      -->
      <id column="orderdetail_id" property="id"/>
      <result column="items_num" property="itemsNum"/>
      <result column="items_id" property="itemsId"/>
      <!-- 商品信息 -->
      <association property="items" javaType="cn.yzu.mybatis.po.Items">
        <id column="item_id" property="id"/>
        <result column="items_name" property="name"/>
        <result column="items_detail" property="detail"/>
      </association>
    </collection>
  </collection>
</resultMap>

延遲加載
  使用延遲加載意義
    在進行數據查詢時,爲了提升數據庫查詢性能,儘可能使用單表查詢,由於單表查詢比多表關聯查詢速度要快。若是查詢單表就能夠知足需求,一開始先查詢單表,當須要關聯信息時,再關聯查詢,當須要關聯信息再查詢這個叫延遲加載。
    mybatis中resultMap提供延遲加載功能,經過resultMap配置延遲加載。
  配置mybatis支持延遲加載

<!-- 全局配置參數 -->
<settings>
  <!-- 延遲加載總開關 -->
  <setting name="lazyLoadingEnabled" value="true" />    
  <!-- 設置按需加載 -->
  <setting name="aggressiveLazyLoading" value="false" />
</settings>

  延遲加載實現思路
    需求查詢訂單及用戶的信息,一對一查詢。剛開始只查詢訂單信息,當須要用戶時調用 Orders類中的getUser()方法執行延遲加載 ,向數據庫發出sql。
    mapper.xml

<!-- 一對一查詢延遲加載 開始只查詢訂單,對用戶信息進行延遲加載 -->
<select id="findOrderUserListLazyLoading" resultMap="orderCustomLazyLoading">
  SELECT orders.*
  FROM orders
</select>

    resultMap

<!-- 一對一查詢延遲加載 的配置 -->
<resultMap type="orders" id="orderCustomLazyLoading">
  <!-- 完成了訂單信息的映射配置 -->
  <!-- id:訂單關聯用戶查詢的惟 一 標識 -->
  <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:延遲加載執行的sql所在的statement的id,若是不在同一個namespace須要加namespace 
    sql:根據用戶id查詢用戶信息 column:關聯查詢的列 property:將關聯查詢的用戶信息設置到Orders的哪一個屬性 -->
  <association property="user" select="cn.yzu.mybatis.mapper.UserMapper.findUserById" column="user_id"></association>
</resultMap>

    測試代碼

// 一對一查詢延遲加載
@Test
public void testFindOrderUserListLazyLoading() throws Exception {
  SqlSession sqlSession = sqlSessionFactory.openSession();
  OrdersMapperCustom ordersMapperCustom = sqlSession.getMapper(OrdersMapperCustom.class);
  List<Orders> list = ordersMapperCustom.findOrderUserListLazyLoading();
  User user = list.get(0).getUser();
  System.out.println(user);
}

  resultType、resultMap、延遲加載使用場景總結
    延遲加載:延遲加載實現的方法多種多樣,在只查詢單表就能夠知足需求,爲了提升數據庫查詢性能使用延遲加載,再查詢關聯信息。mybatis提供延遲加載的功能用於service層。
    resultType
      做用:將查詢結果按照sql列名pojo屬性名一致性映射到pojo中。
      場合:常見一些明細記錄的展現,將關聯查詢信息所有展現在頁面時,此時可直接使用resultType將每一條記錄映射到pojo中,在前端頁面遍歷list(list中是pojo)便可。
    resultMap:使用association和collection完成一對一和一對多高級映射。
      association
        做用:將關聯查詢信息映射到一個pojo類中。
        場合:爲了方便獲取關聯信息可使用association將關聯訂單映射爲pojo,好比:查詢訂單及關聯用戶信息。
      collection
        做用:將關聯查詢信息映射到一個list集合中。
        場合:爲了方便獲取關聯信息可使用collection將關聯信息映射到list集合中,好比:查詢用戶權限範圍模塊和功能,可以使用collection將模塊和功能列表映射到list中。
查詢緩存
  緩存的意義
    將用戶常常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關係型數據庫數據文件)查詢,從緩存中查詢,從而提升查詢效率,解決了高併發系統的性能問題。
  mybatis持久層緩存
    mybatis提供一級緩存和二級緩存,mybatis一級緩存是一個SqlSession級別,sqlsession只能訪問本身的一級緩存的數據,二級緩存是跨sqlSession,是mapper級別的緩存,對於mapper級別的緩存不一樣的sqlsession是能夠共享的。
  一級緩存
    原理:第一次發出一個查詢sql,sql查詢結果寫入sqlsession的一級緩存中,緩存使用的數據結構是一個map<key,value>
      key:hashcode+sql+sql輸入參數+輸出參數(sql的惟一標識)
      value:用戶信息
      同一個sqlsession再次發出相同的sql,就從緩存中取不走數據庫。若是兩次中間出現commit操做(修改、添加、刪除),本sqlsession中的一級緩存區域所有清空,下次再去緩存中查詢不到因此要從數據庫查詢,從數據庫查詢到再寫入緩存。
      每次查詢都先從緩存中查詢:若是緩存中查詢到則將緩存數據直接返回。若是緩存中查詢不到就從數據庫查詢
    一級緩存配置:mybatis默認支持一級緩存不須要配置。
      注意:mybatis和spring整合後進行mapper代理開發,不支持一級緩存,mybatis和spring整合,spring按照mapper的模板去生成mapper代理對象,模板中在最後統一關閉sqlsession。
  二級緩存
    原理:二級緩存的範圍是mapper級別(mapper同一個命名空間),mapper以命名空間爲單位建立緩存數據結構,結構是map<key、value>。每次查詢先看是否開啓二級緩存,若是開啓從二級緩存的數據結構中取緩存數據,若是從二級緩存沒有取到,再從一級緩存中找,若是一級緩存也沒有,從數據庫查詢。
    mybatis二級緩存配置:在覈心配置文件SqlMapConfig.xml中加入<setting name="cacheEnabled" value="true"/>要在你的Mapper映射文件中添加一行:  <cache /> ,表示此mapper開啓二級緩存。
    查詢結果映射的pojo序列化:mybatis二級緩存須要將查詢結果映射的pojo實現 java.io.serializable接口,若是不實現則拋出異常:org.apache.ibatis.cache.CacheException: Error serializing object. Cause: java.io.NotSerializableException: cn.itcast.mybatis.po.User,二級緩存能夠將內存的數據寫到磁盤,存在對象的序列化和反序列化,因此要實現java.io.serializable接口。若是結果映射的pojo中還包括了pojo,都要實現java.io.serializable接口。
    二級緩存禁用:對於變化頻率較高的sql,須要禁用二級緩存,在statement中設置useCache=false能夠禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認狀況是true,即該sql使用二級緩存。<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
    二級緩存的應用場景
      對查詢頻率高,變化頻率低的數據建議使用二級緩存。
      對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可採用mybatis二級緩存技術下降數據庫訪問量,提升訪問速度,業務場景好比:耗時較高的統計分析sql、電話帳單查詢sql等。

相關文章
相關標籤/搜索