mybatis映射器(四)

4.1 映射器的主要元素java

元素名稱 描述 備註
select 查詢語句,最經常使用、最複雜的元素之一 能夠自定義參數,返回結果集等
insert 插入語句 執行後返回一個整數,表明插入的條數
update 更新語句 執行後返回一個整數,表明更新的條數
delete 刪除語句 執行後返回一個整數,表明刪除的條數
parameterMap 定義參數映射關係 即將被刪除的元素,不建議使用
sql 容許定義一部分的SQL,而後在各個地方引用它 例如,一張表列名,咱們能夠一次定義,在多個SQL語句中使用
resultMap 用來描述從數據庫結果集中來加載對象,它是最複雜、最強大的元素 它將提供映射規則
cache 給定命名空間的緩存配置  
cache-ref 其餘命名空間緩存配置的引用  

4.2 select元素算法

4.2.1 概述sql

select元素的配置數據庫

元素 說明 備註
id 它和Mapper的命名空間組合起來是惟一的,提供給mybatis調用 若是命名空間和id組合起來不惟一,mybatis將拋出異常
parameterType 你能夠給出類的全命名,也能夠給出類的別名,但使用別名必須是mybatis內部定義或者自定義的 咱們能夠選擇JavaBean、Map等複雜的參數類型傳遞給SQL
parameterMap 即將廢棄的元素,不討論  
resultType 定義類的全路徑名字,在容許自動匹配的狀況下,結果集將經過JavaBean的規範映射;或定義爲int、double、float等參數;也可使用別名,可是要符合別名規範,不能和resultMap同時使用 它是咱們經常使用的參數之一,好比咱們統計總條數就能夠把它的值設置爲int
resultMap 它是映射集的引用,將執行強大的映射功能,咱們可使用resultType或者resultMap其中的一個,resultMap能夠給予咱們自定義映射規則的機會 它是mybatis最複雜的元素,能夠配置映射規則、級聯、typeHandler等。
flushCatch 它的做用是在調用SQL後,是否要求mybatis清空以前查詢的本地緩存和二級緩存 取值爲布爾值,true/false,默認值爲false
     

4.2.2 簡易數據類型的例子apache

​ 例如,咱們須要統計一個姓氏的用戶數量。咱們應該把姓氏做爲參數傳遞,而將結果設置爲整型返回給調用者。緩存

<select id="countFirstName" parameterType="string" resultType="int">
    select count(*) as total from t_user where name like concat (#{firstName},'%')
</select>

咱們在接口UserDao中定義方法。安全

public int countFirstName(String firstName);

​ 操做步驟:服務器

  • id標出了這條SQLsession

  • parameterType定義參數類型mybatis

  • resultType定義返回值類型

4.2.3 自動映射

​ 有這樣的一個參數autoMappingBehavior,當它不設置爲NONE的時候,mybatis會提供自動映射的功能,只要返回自動映射的功能,只要返回的SQL列名和JavaBean的屬性一致,mybatis就會幫助咱們回填這些字段而無需任何配置,它能夠在很大程度上簡化咱們的配置工做。

​ 自動映射能夠在settings元素中配置autoMappingBehavior屬性值來設置其策略。它包含3個值。

  • NONE,取消自動映射。

  • PARTIAL,只會自動映射,沒有定義嵌套結果映射集的結果集。

  • FULL,會自動映射任意複雜的結果集(不管是否嵌套)。

    默認爲PARTIAL。因此在默認的狀況下,它能夠作到當前對象的映射,使用FULL是嵌套映射,在性能上會降低。

4.2.4 傳遞多個參數

4.2.4.1 使用Map傳遞參數

​ 咱們可使用mybatis提供的Map接口做爲參數來實現它。

<select id="findRoleByMap" parameterType="map" resultMap="roleMap">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 對於RoleDao接口,咱們提供一個方法。

public List<Role> findRoleByMap(Map<String,String>params);

4.2.4.2 使用註解方式傳遞參數

​ 咱們須要使用mybatis的參數註解@Param(org.apache.ibatis.annotations.Param)來實現想要的功能。操做方法是,把RoleDao接口修改成下面的形式。

public List<Role> findRoleByAnnotation(@Param("roleName")String rolename,@Param("note")String note);

​ 把映射器的XML修改成無需定義參數類型

<select id="findRoleByAnnotation" resultMap="roleMap">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 把參數傳遞給後臺的時候,經過@Param提供的名稱mybatis就會知道#{roleName}表明rolename參數,參數的可讀性大大提升了。可是這會引發另外一個麻煩,一條SQL擁有10個參數的查詢,若是咱們都使用@Param方式,那麼參數將十分複雜,可讀性依舊不高,不過mybatis爲咱們提供了JavaBean定義參數的方式來解決這個問題。

4.2.4.3 使用JavaBean傳遞參數

​ 在參數過多的狀況下,mybatis容許組織一個JavaBean,經過簡單的setter和getter方法設置參數,這樣就能夠提升代碼可讀性。

​ 在xml文件裏引入這個JavaBean:

<select id="findRoleByParms" paramterType="com.learn.chapter4.params.RoleParam" resultMap="roleMap">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 同時咱們在RoleDao接口提供一個方法

public List<Role> findRoleByParams(RoleParam params);

4.2.4.4 總結

  • 使用Map傳遞參數。由於Map致使業務可讀性的喪失,從而致使後續擴展和維護的困難,咱們應該在實際的應用中果斷廢棄這樣的傳遞參數的方式。

  • 使用@Param註解傳遞多個參數,這種方式的使用受到參數個數(n)的影響。當n《=5時,它是最佳的傳遞參數的方式,它比用JavaBean更好,由於它更加直觀;當n>5時,多個參數調用將帶來困難。

  • 當參數個數多於5個時,建議使用JavaBean方式。

4.2.5 使用resultMap映射結果集

​ 在映射器中定義resultMap,用resultMap處理更爲複雜的映射。

<resultMap id="roleResultMap" type="com.learn.chapter4.pojo.Role">
    <id property="id" column="id"></id>
    <result property="roleName" column="role_name"></result>
    <result property="note" column="note"></result>
</resultMap>
<select paramterType="long" id="getRole" resultMap="roleResultMap">
    select id,role_name,note from t_role where id = #{id}
</select>

​ 解釋一下resultMap的配置

  • 定義了一個惟一標識(id)爲roleResultMap的resultMap,用type屬性去定義它對應的是哪一個JavaBean(也可使用別名)

  • 經過id元素定義resultResultMap,這個對象表明着使用哪一個屬性做爲其主鍵。result元素定義普通列的映射關係,例如,把SQL結果返回的列role_no和type屬性定義JavaBean的屬性roleNo等作一一對應。

  • 這樣select語句就再也不須要使用自動映射的規則,直接使用resultMap屬性指定roleResultMap便可,這樣mybatis就會使用咱們的自定義映射規則。

4.3 insert元素

4.3.1 概述

​ mybatis會在執行插入以後返回一個整數,以表示你進行操做後插入的記錄數。

insert元素配置詳解

屬性名稱 描述 備註
id 它和Mapper的命名空間組合起來是惟一的,做爲惟一標誌提供給Mybatis調用 如不惟一,mybatis將拋出異常
parameterType 能夠給出類的全命名,也能夠是一個別名,但使用別名必須是mybatis內部定義或者自定義的別名 咱們能夠選擇JavaBean、Map等參數類型傳遞給SQL
parameterMap    
flushCache 在調用SQL後,是否要求mybatis清空以前查詢的本地緩存和二級緩存 取值爲布爾值,默認值爲false
timeout 設置超時參數,等超時的時候將拋出異常,單位爲秒 默認值是數據庫廠商提供的JDBC驅動所設置的秒數
statementType 告訴mybatis使用哪一個JDBC的statement工做,取值爲STATEMENT(statement)、PREPARED(PreparedStatement)和CallableStatement 默認值爲PREPARED
keyProperty 表示1️⃣哪一個列做爲屬性的主鍵。不能和keyProperty同時使用 設置哪一個列爲主鍵,若是是聯合主鍵能夠用逗號將其隔開
useGeneratedKeys 這會令mybatis使用JDBC的getGeneratedKeys方法來取出由數據庫內部生成的主鍵 取值爲布爾值,默認值爲false
keyColumn 指明第幾列是主鍵,不能和keyProperty同時使用,只接受整形參數 同keyProperty
databaseId   提供多種數據庫的支持
lang 自定義語言,可使用第三方語言  
     
     
     

4.3.2 主鍵回填和自定義

​ MySQL裏面的主鍵須要根據一些特殊的規則去生成,在插入後咱們每每須要得到這個主鍵,以便於將來的操做。

​ 使用keyProperty屬性指定是哪一個是主鍵字段,同時使用useGeneratedKeys屬性告訴mybatis這個主鍵是否使用數據庫內置策略生成。所以創建POJO,它能提供setter和getter方法,這樣能使用Mybatis的主鍵回填功能。

插入後自動返回主鍵

<insert id="insertRole" parameterType="role"
        useGeneratedKeys="true" keyProperty="id">
        insert into t_role(role_name,note) value (#{roleName},#{note})
</insert>

​ 這樣咱們傳入role對象就無需設置id的值,mybatis會用數據庫的設置進行處理。

4.4 update和delete

​ mybatis執行完update和delete元素以後會返回一個整數,標出執行後影響記錄條數。

<update parameterType="role" id="updateRole">
update t_role set 
role_name = #{roleName},
    note = #{note}
    where id = #{id}
</update>
​
<delete id = "delete" parameterType="long">
delete from t_role where id = #{id}
</delete>

4.5 參數

​ 經過制定參數的類型去讓對應的typeHandler處理它們。

定義參數屬性的時候,mybatis不容許換行!

4.5.1 參數配置

​ 參數能夠是基本類型也能夠是JavaBean,咱們能夠指定特定的類型,已肯定使用哪一個typeHandler處理它們。

#{age,javaType=int,jdbcType=NUMERIC}
​
<!-- 能夠指定用哪一個typeHandler去處理參數 -->
#{age,javaType=int,jdbcType=NUMERIC,typeHandler}
<!-- 能夠對一些數值型的參數設置其保存的精度 -->
#{price,javaType=double,jdbcType=NUMREIC,numericScale=2}

4.5.2 存儲過程支持

​ 對於存儲過程而言,存在三種參數,輸入參數(IN),輸出參數(OUT),輸入輸出參數(INOUT)。mybatis的參數規則爲其提供了良好的支持。咱們經過制定mode屬性來肯定其是哪種參數,它的選項有三種:IN-OUT-INOUT。當參數設置爲OUT,或者爲INOUT的時候,正如你所但願的同樣,mybatis會將存儲過程返回的結果設置到你制定的參數中。當你返回的是一個遊標(也就是咱們制定JdbcType=CURSOR)的時候,你還須要去設置resultMap以便mybatis將存儲過程的參數映射到對應的類型,這時mybatis就會經過你所設置的resultMap自動爲你設置映射結果。

#{role,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=roleResultMap}

​ 這裏的javaType是可選的,即便你不指定它,mybatis也會自動檢測它。

4.5.3 特殊字符串替換和處理(#和$)

select ${columns} from t_tablename

4.6 sql元素

sql元素的使用

<sql id = "role_columns">
    id , role_name , note
</sql>
<select parameterType = "long" id = "getRole" resultMap = "roleMap">
    select <include refid="role_columns"/> from t_role where id = #{id}
</select>
<select parameterType = "map" id = "findRoles">
    select id,role_name,note from t_role
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>

​ 這裏咱們用sql元素定義了role_columns,它能夠很方便地使用include元素的refid屬性進行引用,從而達到重用的功能。還能夠像下面這樣制定參數:

<sql id = "role_columns">
    #{prefix}.role_no,#{prefix}.role_name,#{prefix}.note
</sql>
<select parameterType = "string" id = "getRole" resultMap = "roleResultMap">
    select
        <include refid = "role_columns">
            <property name = "prefix" value = "r"/>
        </include>
    from t_role r where role_no = #{roleNo}
</select>

​ 這樣就能夠給mybatis加入參數,咱們還能夠這樣給refid一個參數值,由程序制定引入SQL,如:

<sql id = "someinclude">
    select * from<include refid="${tableName}"/>
</sql>

4.7 resultMap結果映射集

​ resultMap是mybatis裏面最複雜的元素。它的做用是定義映射規則、級聯的更新、定製類型轉化器等。resultMap定義的主要是一個結果集的映射關係。mybatis現有的版本只支持resultMap查詢,不支持更新或者保存,更沒必要說級聯的更新、刪除和修改了。

4.7.1 resultMap元素的構成

<resultMap>
    <constructor>  配置構造方法
        <idArg/>
        <arg/>
    </constructor>
    <id/>
    <result/>
    <association/>
    <collection/>
    <discriminator>
        <case/>
    </discriminator>
</resultMap>

其中constructor元素用於配置構造方法。一個pojo可能不存在沒有參數的構造方法,這個時候咱們就可使用constructor進行配置。假設角色類RoleBean不存在沒有參數的構造方法,它的構造方法聲明爲public RoleBean(Integer id,String roleName),那麼咱們須要配置這個結果集,以下:

<resultMap ......>
    <constructor>
    <idArg column = "id" javaType = "int"/>
    <arg column = "role_name" javaType = "string"/>
    </constructor>
......
</resultMap>

​ 這樣mybatis就知道須要用這個構造方法來構造POJO了。

​ id元素是表示哪一個列是主鍵,容許多個主鍵,多個主鍵則稱爲聯合主鍵。result是配置POJO到SQL列名的映射關係。這裏的result和id兩個元素都有以下所示的屬性。

元素名稱 說明 備註
property 映射到列結果的字段或屬性。若是POJO的屬性匹配的是存在的,和給定SQL列名(column元素)相同的,那麼mybatis就會映射到POJO上 可使用導航式的字段,好比訪問一個學生對象(Student)須要訪問學生證(selfcard)的發證日期(issueDate),那麼咱們能夠寫成selfcard.issueDate
column 這裏對應的是SQL的列  
javaType 配置Java的類型 能夠是特定的類徹底限定名或者mybatis上下文的別名
jdbcType 配置數據庫類型 這是一個JDBC的類型,mybatis已經爲咱們作了限定,基本支持全部經常使用的數據庫類型。
typeHandler 類型處理器 容許你用特定的處理器來覆蓋mybatis默認的處理器。這就要指定jdbcType和javaType相互轉化的規則
     

​ 此外還有association、collection和discrimination這些元素,咱們將在級聯那裏詳細討論它們的運用方法。

4.7.2 使用map存儲結果集

​ 通常而言,任何的select語句均可以使用map存儲。以下:

<select id = "findColorByNote" parameterType="string" resultType="map">
    select id,color,note from t_color where note like concat('%',#{note},'%')
</select>

​ 可是使用這種方法意味着可讀性降低,因此不推薦這種方式。

4.7.3 使用POJO存儲結果集

​ 使用POJO是咱們經常使用的方式。一方面咱們可使用自動映射,正如select語句裏論述的同樣。咱們還可使用select語句的屬性resultMap配置映射集合,只是使用前須要配置類的resultMap,以下所示:

<resultMap id="roleResultMap" type="com.learn.chapter4.pojo.Role">
    <id property="id" column="id"/>
    <result property="roleName" column="role_name"/>
    <result property="note" column="note"/>
</resultMap>

​ 映射關係中,id元素表示這個對象的主鍵,property表明着POJO的屬性名稱,column表示數據庫SQL的列名,因而POJO就和數據庫SQL的結果一一對應起開了。接着咱們在映射文件中的select元素裏面作以下所示的配置,即可以使用了。

<select parameterType = "long" id = "getRole" resultMap = "roleResultMap">
    select id , role_name , note from t_role where id = #{id}
</select>

​ 咱們能夠發現SQL語句的列名和roleResultMap的column是一一對應的。使用XML配置的結果集,咱們還能夠配置typeHandler、javaType、jdbcType,可是這條語句配置了resultMap就不能再配置resultType了。

4.7.4 級聯

​ 在數據庫中包含着一對多、一對一的關係。比方說一個角色能夠分配給多個用戶,也能夠只分配給一個用戶。有時候咱們但願角色信息和用戶信息一塊兒顯示出來,這個是很常見的場景,因此會常常碰見這樣的SQL,以下:

select r.*,u.* from t_role r inner join t_user_role ur on r.id = ur.id inner join t_user u on ur.user.id = u.id where r.id = #{id}

​ 這裏的查詢是把角色和用戶的信息都查詢出來,咱們但願的是在角色的信息中多一個屬性,即List<UserBean>userList 這樣取出Role的同時也能夠訪問到它下面的用戶了。咱們把這樣的狀況叫作級聯。

級聯分爲三種:

  • association—》一對一

  • collection—》一對多

  • discriminator—》鑑別器,它能夠根據實際選擇採用哪一個類做爲實例,容許你根據特定的條件去關聯不一樣的結果集。

4.7.4.1 association 一對一級聯

<association property="studentSelfcard" column="id" select="com.learn.chapter4.mapper.StudentSelfcardMapper.findStudentSelfcardByStudentId"/>

​ 這就是經過一次關聯來處理問題。上面這段代碼放在一對一級聯的其中一個xml文件中。select的值填寫的是查詢方法,對應在另外一個xml文件中有一個同名的查詢方法。

4.7.4.2 collection 一對多級聯

4.7.4.3 discrimination鑑別器級聯

<discrimainator javaType="int" column="sex">
    <case value="1" resultMap="maleStudentMap" />
    <case value="2" resultMap="femaleStudentMap" />
</discrimainator>  <!-- 寫在resultMap中 -->
--------------------------------------------
<resultMap id = "maleStudentMap" type = "com.learn.chapter4.po.MaleStudentBean" extends="studentMap">
    <collection property = "studentHealthMaleList" select = "com.learn.chapter4.mapper.StudentHealthMaleMapper.findStudentHealthMaleByStuId"
                column = "id" />
</resultMap>

​ 首先咱們定義了一個discrimination元素,它對應的列(column)是sex,對應的java類型(javaType)爲int,因此纔有了下面這行代碼。

<discrimainator javaType="int" column="sex">

​ 接着,咱們配置了case,這是相似switch語句。這樣咱們就能夠在case裏面引入resultMap。當sex=1(男性)時,引入的是maleStudentMap;當sex=2(女性)時,引入的是femaleStudentMap,而後咱們分別對這兩個resultMap進行定義。

​ 這兩個resultMap的定義是大同小異,它們都擴展了原有的studentMap,因此有了下面這行代碼。

extends="studentMap"

​ 正如類的繼承關係同樣,resultMap也能夠繼承,再加入本身的屬性。

4.7.4.4 性能分析和N+1問題

​ 級聯的優點是可以方便快捷地獲取數據。多層級聯時,建議超過三層關聯時儘可能少用級聯,由於不只用處不大,並且會形成複雜度的增長,不利於理解和維護。

​ 級聯更嚴重的問題,假設有表關聯到student表裏面,那麼能夠想象,咱們還要增長級聯關係到這個結果集裏,那麼級聯關係將會異常複雜。若是咱們採起相似默認的場景那麼有一個關聯咱們就要多執行一次SQL,正如咱們上面的例子同樣,每次取一個Student對象,那麼它全部的信息都會被取出來,這樣會形成SQL執行過多致使性能降低,這就是N+1的問題,爲了解決這個問題咱們應該考慮採用延遲加載的功能。

4.7.4.5 延遲加載

​ 爲了解決N+1問題,mybatis引入了延遲加載的功能,延遲加載功能的意義在於,一開始並不取出級聯數據,只有當使用它了才發送SQL去取回數據。

​ 在mybatis的配置中有兩個全局的參數lazyLoadingEnabled和aggressiveLazyLoading。

lazyLoadingEnabled的含義是是否開啓延遲加載功能。aggressiveLazyLoading的含義是對任意延遲屬性的調用會使帶有延遲加載屬性的對象完整加載;反之,每種屬性將按需加載。

​ aggressiveLazyLoading的默認值爲true,也就是使用層級加載的策略。

​ 當一個功能的兩個對象常常須要一塊兒用時,咱們採用即時加載更好,由於即時加載能夠多條SQL一次性發送,性能高。

​ 配置:settings元素裏面的lazyLoadingEnable值開啓延遲加載,使得關聯屬性都按需加載,而不自動加載。要知道在默認的狀況下它是即時加載的,一旦關聯多,那將形成性能問題!爲了改變這種狀況,咱們能夠把mybatis文件的內容配置爲延遲加載。

<settings>
    <setting name="lazyLoadingEnable" value="true"/>
</settings>

在默認的狀況下mybatis是按層級延遲加載的。

及時加載和延遲加載的混合使用

<association property="studentSelfcard" column="id" fetchType="lazy"
             select="com.learn.chapter.mapper.StudentSelfcardMapper.findStudentSelfcardByStudentId"/>
<collection property="studentLectureList" column="id" fetchType="eager"
            select="com.learn.chapter4.mapper.StundentLectureMapper.findStudentLectureByStuId"/>

延遲加載的實現原理是經過動態代理來實現的。在默認的狀況下,mybatis3.3或者以上版本時,是採用JAVASSIST的動態代理,低版本用的是CGLIB,固然也可使用配置修改。動態代理會生成一個動態代理對象,裏面保存着sql和參數,一旦咱們使用這個代理對象的方法,它會進入到動態代理對象的代理方法裏,方法裏面會經過發送sql和參數,就能夠把對應的結果從數據庫裏查找回來,這即是實現原理。

4.7.4.6 另外一種級聯

​ mybatis還提供了另一種級聯方式,這種方式更加簡單直接,也沒有N+1的問題。簡單來講,就是儘可能經過左鏈接(LEFT JOIN)找到其餘學生的信息找到想要的信息。mybatis容許咱們經過配置來完成這些級聯信息。咱們將經過studentMap2定義的映射規則,來完成這個功能。

<resultMap id = "studentMap2" type="com.learn.chapter4.po.StudentBean">
    <id property="id" column="id"/>
    <result property="cnname" column="cnname"/>
    <result property="sex" column="sex" jdbcType="INTEGER"
            javaType="com.learn.chapter4.enums.SexEnum"
     typeHandler="com.learn.chapter4.typehandler.SexTypeHandler"/>
     <result property="note" column="snote"/>
    
    <association property="studentSefcard" column="id"
                  javaType="com.learn.chapter4.po.StudentSelfcardBean">
        <result property="id" column="ssid"/>
        <result property="studentId" column="id"/>
        ......
    </association>
    
    <collection property="studentLectureList"
                ofType="com.learn.chapter4.po.StudentLectureBean">
        <result property="id" column="ssid"/>
        <result property="studentId" column="id"/>
        ......
        <association property="lecture" column="lecture_id"
                  javaType="com.learn.chapter4.po.LectureBean">
            ......
            </association>
    </collection>
    
    <discriminator javaType="int" column="sex">
        <case value="1" resultMap="maleStudentMap2"/>
        <case value="2" resultMap="femaleStudentMap2"/>
    </discriminator>   
</resultMap>
​
<resultMap id="maleStudentMap2" type="com.learn.chapter4.po.MaleStudentBean"                   extends="studentMap2">
    <collection property="studentHealthMaleList" 
                ofType="com.learn.chapter4.po.StudentHealthMaleBean">
            <id property="id" column="hid" javaType="int"/>
            <result property="checkDate" column="check_date"/>
    </collection>
</resultMap>
​
<resultMap id="femaleStudentMap2" type="com.learn.chapter4.po.FemaleStudentBean" 
            extends="studentMap2">
    <collection property="studentHealthfemaleList" 
                ofType="com.learn.chapter4.po.StudentHealthFemaleBean">
        <id property="id" column="hid" javaType="int" />
        <result property="checkDate" column="check_date" />
    </collection>
</resultMap>

說明上述代碼:

  • 第一個association定義的JavaType屬性告訴了mybatis,將使用哪一個類去映射這些字段。第二個associationcolumn定義的是lecture_id,說明用SQL中的字段lecture_id去關聯結果。

  • ofType屬性定義的是collection裏面的泛型是什麼java類型,mybatis會拿你定義的java類和結果集作映射。

  • discrimination則是根據sex的結果來判斷使用哪一個類作映射。它決定了使用男生健康表,仍是女生健康表。

 

​ 這裏咱們看到了一條SQL就能完成映射,可是這條SQL有些複雜。其次咱們是否須要在一次查詢就導出那麼多的數據,這會不會形成資源浪費,同時也給維護帶來必定的困難,這些問題都須要各位同窗在實際工做中去考量。

4.8 緩存cache

​ 緩存是互聯網系統經常用到的,其特色是將數據保存在內存中。目前流行的緩存服務器有MongoDB、Redis、Eccache等。緩存是在計算機內存上保存的數據,在讀取的時候無需再從磁盤讀入,所以具有快速讀取和使用的特色,若是緩存命中率高,那麼能夠極大地提升系統的性能。若是緩存命中率很低,那麼緩存就不存在使用的意義了,因此使用緩存的關鍵在於存儲內容訪問的命中率。

4.8.1 系統緩存(一級緩存和二級緩存)

​ mybatis對緩存提供支持,可是在沒有配置的默認的狀況下,它只開啓一級緩存(一級緩存只是相對於同一個sqlsession而言)。

​ 因此在參數和SQL徹底同樣的狀況下,咱們使用sqlSession第一次查詢後,mybatis會將其放在緩存中,之後再查詢的時候,若是沒有聲明須要刷新,而且緩存沒超時的狀況下,SqlSession都只會取出當前緩存的數據,而不會在此發送SQL到數據庫。

​ 可是若是你使用的是不一樣的SqlSession對象,由於不一樣的SqlSession都是相互隔離的,因此用相同的Mapper、參數和方法,它仍是會再次發送SQL到數據庫去執行,執行後返回結果。

​ 爲了克服多個SqlSession之間的相互隔離問題,每每須要配置二級緩存,使得緩存在SqlSessionFactory層面上可以提供給各個SqlSession對象共享。而SqlSessionFactory層面上的二級緩存是不開啓的,二級緩存的開啓須要進行配置,實現二級緩存的時候,mybatis要求返回的POJO必須是可序列化的,也就是要求實現serializable接口,配置的方法很簡單,只須要在映射XML文件配置就能夠開啓緩存了。

<cache/>

​ 這樣的一個語句裏面,不少設置是默認的,若是咱們只是這樣配置,那麼就意味着:

  • 映射語句文件中的全部select語句將會被緩存

  • 映射語句文件中的全部insert、update和delete語句會刷新緩存

  • 緩存會使用默認的Least Recently Used(LRU,最近最少使用的)算法來收回。

  • 根據時間表,好比No Flush Interval,(CNFI,沒有刷新間隔),緩存不會以任什麼時候間順序來刷新。

  • 緩存會存儲列表集合或對象(不管查詢方法返回什麼)的1024個引用。

  • 緩存會被視爲是read/write(可讀/可寫)的緩存,意味着對象檢索是不共享的,並且能夠安全的被調用者修改,不干擾其餘調用者或線程所作的潛在修改。

    添加了這個配置後,還須要作一件事情,不然就會出現異常。

    就是mybatis要返回的POJO對象要實現Serializable接口,不然它就會拋出異常。

 

配置緩存

<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
  • eviction:表明的是緩存回收策略,目前mybatis提供如下策略。

    • LRU,最近最少使用的,移除最長時間不用的對象。

    • FIFO,先進先出,按對象進入緩存的順序來移除它們。

    • SOFT,軟引用,移除基於垃圾回收器狀態和軟引用規則的對象。

    • WEAK,弱引用,更積極地移除基於垃圾收集器狀態和弱引用規則的對象。這裏採用的是LRU,移除最長時間不用的對象。

  • flushInterval:刷新間隔時間,單位爲毫秒,這裏配置的是100秒刷新,若是你不配置它,那麼當SQL被執行的時候纔會去刷新緩存。

  • size:引入數目,一個正整數,表明緩存最多能夠存儲多少個對象,不宜設置過大。設置過大會致使內存溢出。這裏配置的是1024個對象。

  • readOnly:只讀,意味着緩存數據只能讀而不能修改,這樣設置的好處是咱們能夠快速讀取緩存,缺點是咱們沒有辦法修改緩存,它的默認值爲false,不容許咱們修改。

 

4.8.2 自定義緩存

系統緩存是mybatis應用機器上的本地緩存,可是在大型服務器上,會使用各種不一樣的緩存服務器,這個時候咱們能夠定製緩存,好比Redis緩存。咱們須要實現mybatis爲咱們提供的接口org.apache.ibatis.cache.Cache。

緩存接口簡介

//獲取緩存編號
String getId();
//保存key值緩存對象
void putObject(Object key,Object value);
//經過key獲取緩存對象
Object getObject(Object key);
//經過key刪除緩存對象
Object removeObject(Object key);
//清空緩存
void clear();
//獲取緩存對象大小
int getSize();
//獲取緩存的讀寫鎖
ReadWriteLock getReadWriteLock();