天天看點

Mybatis多表關聯查詢、懶加載、緩存

Mybatis多表關聯查詢、懶加載、緩存

多表關聯查詢:

手動處理映射關系:

資料庫表格和java類之間的對應關系

  1. 自動處理映射關系

    自動根據java類的屬性名和資料庫的字段名封裝資料 要求字段名和屬性名相同

  2. 手動處理映射關系

Java中的實體類和資料庫表格的字段名不同 怎麼處理

  1. 字段使用别名 缺點 SQL語句複用度低 無法處理多變關聯查詢問題
  2. 定義映射關系 映射關系可以在多處使用 可以處理資料庫表格之間一對一 一對多 多對多的關系

映射的xml

<!--手動映射;type是傳回值,傳回的是Emp對象-->
<resultMap id="empresultMap" type="emp">
    <!--column是在資料庫中對應的字段名;property是實體類中的字段名-->
    <id column="empno" property="id"></id>
    <result column="ename" property="name"></result>
</resultMap>
<!--id:方法名;resultMap就是上面的手動映射的id-->
<select id="findEmpByEmpno" resultMap="empresultMap">
    select * from emp where empno = #{empno}
</select>
           
一對一的關系處理:

什麼時候用到這個關系呢?當從資料庫中查詢出的結果是兩個表的資料的時候(查詢的結果隻是一條資料),需要将結果封裝為兩個對象的屬性。

如下有兩個表Emp,Dept;

1.需要将Dept對象組合為Emp對象的屬性

public class Emp {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;
    public Dept dept;
}
           
public class Dept {
    private Integer deptno;
    private String dname;
    private String loc;
}
           

2.xml映射檔案

<resultMap id="empJoinDept" type="emp">
    <!--手動處理映射關系,column資料庫的字段名與是一類的字段名property相對應-->
    <id column="empno" property="empno"></id>
    <result column="ename" property="ename"></result>
    <result column="job" property="job"></result>
    <result column="mgr" property="mgr"></result>
    <result column="hiredate" property="hiredate"></result>
    <result column="sal" property="sal"></result>
    <result column="comm" property="comm"></result>
    <result column="deptno" property="deptno"></result>
    <!--處理組合的另一個實體類,property是emp中的屬性字段名,javaType是Dept實體類的别名;将emp中的dept屬性組合Dept實體類-->
    <association property="dept" javaType="dept">
        <id column="deptno" property="deptno"></id>
        <result column="dname" property="dname"></result>
        <result column="loc" property="loc"></result>
    </association>

</resultMap>

<select id="findEmpJoinDept" resultMap="empJoinDept">
    SELECT *
    FROM emp e LEFT JOIN dept d ON e.DEPTNO=d.DEPTNO
    WHERE e.EMPNO = #{empno}
</select>
           
一對多的關系處理:

什麼時候用到這個關系呢?當從資料庫中查詢出的結果是兩個表的資料的時候(查詢的結果是多條資料,其中一個表的資料是相同的,隻有另一個表的資料不相同時),需要将結果封裝為兩個對象的屬性(資料不同的傳回的應該是一個集合)。

如下有兩個表Emp,Dept(查詢的結果Dept的屬性相同,Emp的屬性有多個)

1.需要将Emp對象組合為Dept對象的屬性

public class Dept {
    private Integer deptno;
    private String dname;
    private String loc;
    private List<Emp> empList;//Emp查詢出多條資料,是以傳回的是集合
}
           
public class Emp {
	private Integer empno;
	private String ename;
	private String job;
	private Integer mgr;
	private Date hiredate;
	private Double sal;
	private Double comm;
	private Integer deptno;
}
           

映射的xml檔案

<!--type是傳回值,dept是實體類Dept的别名-->
<resultMap id="deptJoinEpm" type="dept">
    <!--column是資料庫中字段的名稱,property是實體類中屬性的名稱-->
    <id column="deptno" property="deptno"></id>
    <result column="dname" property="dname"></result>
    <result column="loc" property="loc"></result>
    <!--property是dept屬性中的字段名(組合的Emp對象名),ofType是集合中的每個元素的類型(傳回的是一個Emp對象的集合)-->
    <collection property="empList" ofType="emp">
        <id column="empno" property="empno"></id>
        <result column="ename" property="ename"></result>
        <result column="job" property="job"></result>
        <result column="mgr" property="mgr"></result>
        <result column="hiredate" property="hiredate"></result>
        <result column="sal" property="sal"></result>
        <result column="comm" property="comm"></result>
        <result column="deptno" property="deptno"></result>
    </collection>
</resultMap>

<select id="findDeptJoinEmp" resultMap="deptJoinEpm">
    SELECT *
    FROM dept d LEFT JOIN emp e ON d.deptno =e.DEPTNO
    WHERE d.deptno= #{deptno}
</select>
           
多對多關系處理:

當表之間存在多對多關系時;會将兩個表的主鍵作為第三張表的主鍵;可以将多對多的關系變為一對多和一對一。

如下xml映射檔案

<resultMap id="empJoinProject" type="emp">
    <!--emp本身的8個屬性-->
    <id column="empno" property="empno"></id>
    <result column="ename" property="ename"></result>
    <result column="job" property="job"></result>
    <result column="mgr" property="mgr"></result>
    <result column="hiredate" property="hiredate"></result>
    <result column="sal" property="sal"></result>
    <result column="comm" property="comm"></result>
    <result column="deptno" property="deptno"></result>
    <!--property Emp的屬性;ofType封裝的實體類的别名;封裝第三張表的兩個屬性以及一對一關系的實體類的組合-->
    <collection property="workrecordes" ofType="workrecorde">
        <id column="empno" property="empno"></id>
        <id column="pid" property="pid"></id>
        <association property="project" javaType="project">
            <id column="pid" property="pid"/>
            <result column="pname" property="pname"/>
        </association>
    </collection>
</resultMap>
<select id="findEmpJoinProject" resultMap="empJoinProject">
    SELECT *
    FROM emp e LEFT JOIN workrecorde w ON e.EMPNO=w.empno
    LEFT JOIN project p ON w.pid=p.pid
    WHERE e.EMPNO=#{empno}
</select>

           

SQL語句之間的互相調用

進行多表查詢的時候,可以調用另一xml檔案進行幫助查詢

如下是主Xml檔案

<resultMap id="workrecordJoinProject" type="workrecorde">
    <id column="empno" property="empno"></id>
    <id column="pid" property="pid"></id>
    <!--column将上面查出的pid作為參數,調用下面的映射檔案-->
    <association property="project" javaType="project" column="pid" select="mapper.ProjectMapper.finfProjectJoinEmps">
    </association>
</resultMap>
<select id="findWorkrecordeJoinProjectByEmpno" resultMap="workrecordJoinProject">
    SELECT *
    FROM workrecorde WHERE empno=#{empno}
</select>
           

被調用的xml檔案

<mapper namespace="mapper.ProjectMapper">
    <!--手動映射;type是傳回值,傳回的是Emp對象-->
    <select id="finfProjectJoinEmps" resultType="project">
        select * from  project where pid = #{pid}
</select>
</mapper>
           
懶加載;也叫延遲加載的設定

當進行多表查詢進行sql之間的互相調用時,會産生很多sql語句一起加載,這時可以再核心配置檔案中設定延遲加載,即什麼時候用到所需要的資料的時候再執行sql語句

mybatis.xml核心配置檔案的設定,設定下面兩個屬性

<settings>
    <!--全局啟用或禁用延遲加載。當禁用時, 所有關聯對象都會即時加載。 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--當啟用時, 有延遲加載屬性的對象在被 調用時将會完全加載任意屬性。否則, 每種屬性将會按需要加載。 -->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>
           

mybatis緩存

一級緩存:

SQLSession對象内部的緩存;在同一個SqlSession對象中調用的方法和傳入的參數一緻時,多次查詢,mybatis預設使用緩存技術減少查詢的次數

什麼情況下 一級緩存有作用 (一級緩存預設開啟)

  • 同一個SQLSession
  • 調用的方法 SQL語句的名稱空間和id相同
  • 同一個參數

SqlSession内部的緩存如何實作:

​ 通過Map集合實作緩存

​ 鍵:名稱空間+SQL的id+參數+SQLSession的哈希碼

​ 值:SQL語句的查詢結果

優缺點:

  1. 減少了查詢的次數
  2. 造成資料的髒讀

一級緩存産生了髒讀怎麼辦?(一級緩存預設開啟,無法關閉)

  1. 手動重新整理,清空緩存sqlSession.clearCache()
  2. 發生了增删改(之後的送出動作),也會預設清空緩存
二級緩存:

​ 一級緩存作用于一個SqlSession

​ 二級緩存作用于一個namespace

​ 以名稱空間作為緩存的标志 每一名稱空間都有自己獨立的緩存區域

​ 多個mapper對象是否可以是同一個名稱空間? 可以 二級緩存可以跨 mapper對象 必須同一個接口的mapper對象

​ 多個SQLSession對象 是否可以使用同一個名稱空間? 可以二級緩存 可以跨SQLSession對象

二級緩存不是自動開啟的:

​ 因為在項目中 SQLsession對象應該避免大量建立

​ 同一個接口下 的mapper代理對象應該避免大量建立 是以二級緩存 用的不多

如何開啟耳機緩存?

  • 核心配置檔案中開啟二級緩存
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
           
  • Mapper映射檔案中 開啟針對于某個名稱空間的二級緩存
<mapper namespace="mapper.WorkrecordMapper">
    <cache/>
</mapper>
           
  • 針對于SQL語句是否使用二級緩存的設定
<!--useCache 控制目前sql語句是否使用二級緩存,預設是true,預設開啟;flushCache 控制目前的sql語句執行一次後,是否會重新整理緩存,預設是false-->
<select id="findWorkrecordeJoinProjectByEmpno" resultMap="workrecordJoinProject" useCache="true" flushCache="false">
    SELECT *
    FROM workrecorde WHERE empno=#{empno}
</select>
           
注意:開啟二級緩存是實體類要實作系列化接口

預設的屬性和參數

參數

flushInterval(重新整理間隔)可以被設定為任意的正整數,而且它們代表一個合理的毫秒形式的時間段。預設情況是不設定,也就是沒有重新整理間隔,緩存僅僅調用語句時重新整理。

size(引用數目)可以被設定為任意正整數,要記住你緩存的對象數目和你運作環境的可用記憶體資源數目。預設值是1024。

readOnly(隻讀)屬性可以被設定為true或false。隻讀的緩存會給所有調用者傳回緩存對象的相同執行個體。是以這些對象不能被修改。這提供了很重要的性能優勢。可讀寫的緩存會傳回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,是以預設是false。

如下例子:

這個更進階的配置建立了一個 FIFO 緩存,并每隔 60 秒重新整理,存數結果對象或清單的 512 個引用,而且傳回的對象被認為是隻讀的,是以在不同線程中的調用者之間修改它們會導緻沖突。可用的收回政策有, 預設的是 LRU:

  • LRU – 最近最少使用的:移除最長時間不被使用的對象。
  • FIFO – 先進先出:按對象進入緩存的順序來移除它們。
  • SOFT – 軟引用:移除基于垃圾回收器狀态和軟引用規則的對象。
  • WEAK – 弱引用:更積極地移除基于垃圾收集器狀态和弱引用規則的對象。