Mybatis多表關聯查詢、懶加載、緩存
多表關聯查詢:
手動處理映射關系:
資料庫表格和java類之間的對應關系
-
自動處理映射關系
自動根據java類的屬性名和資料庫的字段名封裝資料 要求字段名和屬性名相同
- 手動處理映射關系
Java中的實體類和資料庫表格的字段名不同 怎麼處理
- 字段使用别名 缺點 SQL語句複用度低 無法處理多變關聯查詢問題
- 定義映射關系 映射關系可以在多處使用 可以處理資料庫表格之間一對一 一對多 多對多的關系
映射的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語句的查詢結果
優缺點:
- 減少了查詢的次數
- 造成資料的髒讀
一級緩存産生了髒讀怎麼辦?(一級緩存預設開啟,無法關閉)
- 手動重新整理,清空緩存sqlSession.clearCache()
- 發生了增删改(之後的送出動作),也會預設清空緩存
二級緩存:
一級緩存作用于一個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 – 弱引用:更積極地移除基于垃圾收集器狀态和弱引用規則的對象。