MyBatis詳解4.映射器Mapper

字節(jié)跳動(dòng)飛書內(nèi)推!
北京机隙、杭州蜘拉、武漢、廣州有鹿、深圳旭旭、上海,六大城市等你來投葱跋。
感興趣的朋友可以私我咨詢&內(nèi)推持寄,也可以通過鏈接直接投遞源梭!
海量HC,極速響應(yīng)稍味,快來和我成為同事吧废麻。
今日頭條、抖音模庐、Tik Tok也可以內(nèi)推~

點(diǎn)擊進(jìn)入我的博客

MyBatis詳解1.概述
MyBatis詳解2.MyBatis使用入門
MyBatis詳解3.MyBatis配置詳解
MyBatis詳解4.映射器Mapper
MyBatis詳解5.動(dòng)態(tài)SQL
MyBatis詳解6.MyBatis技術(shù)內(nèi)幕
MyBatis詳解7.插件
MyBatis詳解8.集成Spring

1 概述

MyBatis是針對(duì)映射器構(gòu)造的SQL構(gòu)建的輕量級(jí)框架烛愧,并且通過配置生成對(duì)應(yīng)的JavaBean返回給調(diào)用者,而這些配置主要便是映射器赖欣。

元素名稱 描述 備注
select 查詢語句屑彻,最常用、最復(fù)雜的元素之一 可以自定義參數(shù),返回結(jié)果集等
insert 插入語句 執(zhí)行后返回一個(gè)整數(shù)顶吮,代表插入的條數(shù)
update 更新語句 執(zhí)行后返回一個(gè)整數(shù)社牲,代表更新的條數(shù)
delete 刪除語句 執(zhí)行后返回一個(gè)整數(shù),代表刪除的條數(shù)
parameterMap 定義參數(shù)映射關(guān)系 即將被刪除的元素悴了,不建議大家使用
sql 允許定義一部分的SQL搏恤,然后在各個(gè)地方引用
resultMap 用來描述從數(shù)據(jù)庫結(jié)果集中來加載對(duì)象,它是它將提供映射規(guī)則最復(fù)雜湃交、最強(qiáng)大的元素 它提供映射規(guī)則
cache 給定命名空間的緩存配置
cache-ref 其他命名空間緩存配置的引用

2 引入映射器

通過文件類路徑引入
    <mappers>
        <mapper resource="mappers/TbNameMapper.xml"/>
    </mappers>
通過包名引入映射器
public interface TbNameMapper {
    @Select("SELECT * FROM tb_name WHERE id = #{id}")
    UserDomain select(Long id);
}
    <mappers>
        <package name="com.ankeetc.spring.mapper"/>
    </mappers>
用類注冊(cè)引入映射器
    <mappers>
        <mapper class="com.ankeetc.spring.mapper.TbNameMapper"/>
    </mappers>

3 select元素

3.1 select元素的配置元素

元素 說明 備注
id 它和 Mapper的命名空間組合起來是唯一的熟空,提供給MyBatis調(diào)用 如過命名空間和id組合起來不唯一將拋出異常
parameterType 可以給出類的全命名,也可以給出類的別名搞莺,但使用別名必須是MyBatis內(nèi)部定義或者自定義的 可以選擇JavaBean息罗、Map等復(fù)雜的參數(shù)類型傳遞給SQL
parameterMap 即將廢棄的元素,我們不再討論它
resultType 定義類的全路徑才沧,在允許自動(dòng)匹配的情況下迈喉,結(jié)果集將通過JavaBean的規(guī)范映射;也可以定義為int温圆、 double等參數(shù)挨摸;也可以使用別名,但是要符合別名規(guī)范岁歉;不能和 resultMap同時(shí)使用 常用參數(shù)之一
resultMap 它是映射集的引用得运,將執(zhí)行強(qiáng)大的映射功能 可以配置映射規(guī)則、級(jí)聯(lián)锅移、typeHandler等
flushCache 它的作用是在調(diào)用SQL后熔掺,是否要求清空之前查詢的本地緩存和二級(jí)緩存 布爾值,默認(rèn)值為 false
useCache 啟動(dòng)二級(jí)緩存的開關(guān)非剃,是否將此次結(jié)果緩存 布爾值瞬女,默認(rèn)值為tue
timeout 設(shè)置超時(shí)參數(shù),等超時(shí)的時(shí)候?qū)伋霎惓E耍瑔挝粸槊?/td> 默認(rèn)值是數(shù)據(jù)庫廠商提供的JDBC驅(qū)動(dòng)所設(shè)置的秒數(shù)
fetchSize 獲取記錄的總條數(shù)設(shè)定 默認(rèn)值是數(shù)據(jù)庫廠商提供的JDBC驅(qū)動(dòng)所設(shè)置的條數(shù)
statementType 告訴MyBatis使用哪個(gè)JDBC的Statement工作,取值為STATEMENT、PREPARED疯坤、CallableStatement 默認(rèn)為PREPARED
resultSetType 這是對(duì)JDBC的resultSet接口而言报慕,它的值包括 FORWARD_ONLY(游標(biāo)允許向前訪問)、 SCROLL_SENSITIVE(雙向滾動(dòng)压怠,但不及時(shí)更新眠冈,就是如果數(shù)據(jù)庫里的數(shù)據(jù)修改過,并不在resultSet中反應(yīng)出來)菌瘫、 SCROLL_ INSENSITIVE(雙向滾動(dòng)蜗顽,并及時(shí)跟蹤數(shù)據(jù)庫的更新,以便更改 resultSet中的數(shù)據(jù)) 默認(rèn)值是數(shù)據(jù)庫廠商提供的JDBC驅(qū)動(dòng)所設(shè)置的
databaseId 參考第三章
resultOrdered 這個(gè)設(shè)置僅適用于嵌套結(jié)果集select語句雨让。如果為true雇盖,就是假設(shè)包含了嵌套結(jié)果集或者是分組了。當(dāng)返回一個(gè)主結(jié)果行的時(shí)候栖忠,就不能對(duì)前面結(jié)果集的引用崔挖。這就確保了在獲取嵌套的結(jié)果集的時(shí)候不至于導(dǎo)致內(nèi)存不夠用 布爾值,默認(rèn)為false
resultSets 適合于多個(gè)結(jié)果集的情況庵寞,它將列出執(zhí)行SQL后每個(gè)結(jié)果集的名稱狸相,每個(gè)名稱之間用逗號(hào)分隔 很少使用

3.2 自動(dòng)映射

mybatis-config.xml文件中<settings>標(biāo)簽有一個(gè)參數(shù)autoMapping Behavior,當(dāng)它不設(shè)置為NONE的時(shí)候捐川,MyBatis會(huì)提供自動(dòng)映射的功能脓鹃,只要返回的SQL列名和JavaBean的屬性一致,MyBatis就會(huì)幫助我們回填這些字段而無需任何配置古沥,它可以在很大程度上簡化我們的配置工作瘸右。

自動(dòng)映射策略

自動(dòng)映射可以在settings元素中配置autoMappingBehavior屬性值來設(shè)置其策略:

  • NONE,取消自動(dòng)映射渐白。
  • PARTIAL(默認(rèn)值)尊浓,只會(huì)自動(dòng)映射,沒有定義嵌套結(jié)果集映射的結(jié)果集纯衍。
  • FULL栋齿,會(huì)自動(dòng)映射任意復(fù)雜的結(jié)果集(無論是否嵌套),在性能上會(huì)下降襟诸。
下劃線轉(zhuǎn)駝峰

如果你的數(shù)據(jù)庫是規(guī)范命名的瓦堵,即每一個(gè)單詞都用下劃線分隔,POJO采用駝峰式命名方法歌亲,那么可以在<settings>設(shè)置 mapUnderscoreToCamelCase為true菇用,這樣就可以實(shí)現(xiàn)從數(shù)據(jù)庫到POJO的自動(dòng)映射了。

3.3 傳遞多個(gè)參數(shù)

使用Map傳遞多個(gè)參數(shù)

可是使用Map來傳遞多個(gè)參數(shù)陷揪,如下所示惋鸥。這種方式的不足之處是業(yè)務(wù)關(guān)聯(lián)性不強(qiáng)杂穷,造成可讀性下降。

public interface TbNameMapper {
    UserDomain select(Map<String, Object> map);
}
    <!-- parameterType也可以縮寫為map -->
    <select id="select" resultType="com.ankeetc.spring.domain.UserDomain" parameterType="java.util.Map" >
        SELECT * FROM tb_name WHERE id = #{id}
    </select>
使用注解傳遞多個(gè)參數(shù)

可是使用@Param來實(shí)現(xiàn)傳遞多個(gè)參數(shù)卦绣,此時(shí)無需在XML中配置parameterType耐量。

public interface TbNameMapper {
    UserDomain select(@Param("id") Long id);
}
    <!-- 無須parameterType -->
    <select id="select" resultType="com.ankeetc.spring.domain.UserDomain" >
        SELECT * FROM tb_name WHERE id = #{id}
    </select>
也可以使用JavaBean傳遞多個(gè)參數(shù)
public interface TbNameMapper {
    UserDomain select(UserDomain userDomain);
}
    <select id="select" resultType="com.ankeetc.spring.domain.UserDomain" parameterType="com.ankeetc.spring.domain.UserDomain">
        SELECT * FROM tb_name WHERE id = #{id}
    </select>

3.4 使用resultMap映射結(jié)果集

如果自動(dòng)映射滿足不了需求,此時(shí)需要通過resultMap來映射滤港±妊眩可以創(chuàng)建一個(gè)<resultMap>元素,然后通過<select>中的resultMap屬性來引用resultMap溅漾,后面會(huì)詳細(xì)分析<resultMap>元素山叮。

4 insert元素

4.1 insert元素的配置元素

insert的很多配置元素是和select一樣的,就不再贅述添履,下面列出select中沒有的屬性

屬性 說明 備注
keyProperty 表示以哪個(gè)列作為屬性的主鍵屁倔,不能和keyColumn同時(shí)使用 設(shè)置哪個(gè)鍵為主鍵,聯(lián)合主鍵可以用逗號(hào)將其隔開
keyColumn 表示第幾列是主鍵缝龄,不能和keyProperty同時(shí)使用 聯(lián)合主鍵可以用逗號(hào)將其隔開
useGeneratedKeys 這會(huì)令MyBatis使用JDBC的getGeneratedKeys方法來取出由數(shù)據(jù)庫內(nèi)部生成的主鍵汰现,但是使用它就必須要給keyProperty或者 keyColumn賦值 布爾值,默認(rèn)為false
lang 自定義語言,可使用第三方語言 使用得較少叔壤,不做介紹

4.2 主鍵回填

  1. 設(shè)置useGeneratedKeys=true
  2. 設(shè)置keyProperty為需要回填的bean的屬性
    <insert id="insert" parameterType="com.ankeetc.spring.domain.UserDomain" useGeneratedKeys="true" keyProperty="id" >
        INSERT INTO tb_name (name) VALUES (#{name})
    </insert>
    public static void func() {
        // 獲取主鍵值
        UserDomain userDomain = new UserDomain("LOL");
        tbNameMapper.insert(userDomain);
        System.out.println(userDomain.getId());
    }

4.3 使用selectKey元素

在insert元素中可以使用selectKey元素來對(duì)回填的主鍵進(jìn)行修改

    <insert id="insert" parameterType="com.ankeetc.spring.domain.UserDomain" useGeneratedKeys="true" keyProperty="id" >
        <!-- 在原有id的基礎(chǔ)上加2 -->
        <selectKey keyProperty="id" resultType="int" order="AFTER">
            SELECT (max(id) + 2) AS newId FROM tb_name
        </selectKey>
        INSERT INTO tb_name (name) VALUES (#{name})
    </insert>

5 update和delete元素

這兩個(gè)元素和insert元素基本一樣瞎饲,都會(huì)返回一個(gè)整數(shù)表示此次執(zhí)行影響的行數(shù)。

6 參數(shù)

6.1 參數(shù)配置

我們可以傳入一個(gè)簡單的參數(shù)如基本類型和字符串炼绘;也可以傳入JavaBean嗅战;還可以指定特定的類型,以確定使用哪個(gè) typeHandler處理它們俺亮。定義參藪屬性的時(shí)候驮捍,MyBatis不允許換行

  • #{age, javaType=int, jdbcType=NUMERIC}
  • 指定typeHandler:#{age, javaType=int, jdbcType=NUMERIC, typeHandler=MyTypeHandler}
  • 數(shù)值型的參數(shù)設(shè)置其保存的精度:#{price, javaType=double, jdbcType=NUMERIC, numericScale=2}

6.2 支持存儲(chǔ)過程

  1. 存儲(chǔ)過程存在3種參數(shù)脚曾,通過指定mode屬性來確定是哪一種參數(shù):IN东且、OUT、INOUT本讥。
  2. 當(dāng)參數(shù)設(shè)置為OUT珊泳、INOUT的時(shí)候,MyBatis會(huì)將存儲(chǔ)過程返回的結(jié)果設(shè)置到你制定的參數(shù)中拷沸。
  3. 當(dāng)你返回的是一個(gè)游標(biāo)(JdbcType=CURSOR)的時(shí)候色查,還需要去設(shè)置resultMap以便MyBatis將存儲(chǔ)過程的參數(shù)映射到對(duì)應(yīng)的類型。

6.3 特殊字符串替換和處理

有時(shí)候我們需要的是傳遞SQL語句的本身撞芍,而不是SQL所需要的參數(shù)秧了,MyBatis也對(duì)這樣的場景進(jìn)行了支持,這些是Hibernate難以做到的序无。例如验毡,我們需要傳遞變量columns="name, value"給SQL衡创,讓其組裝成為SQL語句,而不想被像普通參數(shù)一樣被處理晶通,這個(gè)時(shí)候可以用${param}來實(shí)現(xiàn)钧汹。

public static void func() {
    UserDomain userDomain = tbNameMapper.select("name, value", 18L);
}
public interface TbNameMapper {
    UserDomain select(@Param("columns") String columns, @Param("id") Long id);
}
<select id="select" resultType="com.ankeetc.spring.domain.UserDomain">
    SELECT ${columns} FROM tb_name WHERE id = #{id}
</select>

7 sql元素

sql元素的意義,在于我們可以定義一串SQL語句的組成部分录择,其他的語句可以通過引
用來使用它。這樣就可以實(shí)現(xiàn)一處定義多處引用碗降,大大減少工作量隘竭。

    <sql id="sql1">
        name, value
    </sql>
    <select id="select" resultType="com.ankeetc.spring.domain.UserDomain">
        SELECT 
        <include refid="sql1"/>
        FROM tb_name WHERE id = #{id}
    </select>

8 resultMap結(jié)果映射集

resultMap的作用是定義映射規(guī)則、級(jí)聯(lián)的更新讼渊、定制類型轉(zhuǎn)化器等动看,主要作用是一個(gè)結(jié)果集的映射關(guān)系。

8.1 resultMap元素里的元素

resultMap里的元素
  • constructor元素用于配置構(gòu)造方法爪幻,一個(gè)POJO可能不存在無參數(shù)的構(gòu)造方法菱皆,這個(gè)時(shí)候我們就可以使用 constructor進(jìn)行配置。
  • id元素是表示哪個(gè)列是主鍵挨稿,允許多個(gè)主鍵構(gòu)成聯(lián)合主鍵仇轻。
  • result是配置POJO到SQL列名的映射關(guān)系。
  • 級(jí)聯(lián)屬性association奶甘、discriminator篷店、collection后面詳細(xì)介紹

8.2 使用POJO存儲(chǔ)結(jié)果集

我們?cè)趕elect中使用map存儲(chǔ)結(jié)果集,這樣雖然可以匹配所有結(jié)果集臭家,但會(huì)造成可讀性的下降疲陕,最好的方式是使用POJO。我們可以在select元素中使用resultMap屬性引用我們定義好的resultMap元素钉赁。

    <!-- id是resultMap的唯一標(biāo)識(shí)蹄殃,type是對(duì)應(yīng)POJO的全路徑名 -->
    <resultMap id="base_result_map" type="com.ankeetc.spring.domain.UserDomain">
        <id property="id" column="id" javaType="Long" jdbcType="BIGINT"/>
        <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
        <result property="value" column="value" javaType="Integer" jdbcType="INTEGER"/>
    </resultMap>

    <select id="select" resultMap="base_result_map">
        SELECT * FROM tb_name WHERE id = #{id}
    </select>

8.3 級(jí)聯(lián)屬性

在數(shù)據(jù)庫中包含著一對(duì)一、一對(duì)多你踩、多對(duì)多的關(guān)系诅岩,比如一個(gè)數(shù)據(jù)庫中有一張班級(jí)表:

  • 一對(duì)一的關(guān)系:班級(jí)表里每個(gè)班級(jí)都有一個(gè)班主任
  • 一對(duì)多的關(guān)系:每個(gè)班級(jí)有多個(gè)學(xué)生
  • 多對(duì)多的關(guān)系:一個(gè)老師既可以是多個(gè)班級(jí)的老師,一個(gè)班級(jí)也可以有多個(gè)老師(在實(shí)際中姓蜂,多對(duì)多的關(guān)系應(yīng)用不多按厘,因?yàn)樗鼤?huì)增加理解和關(guān)聯(lián)的復(fù)雜度,更好的方法是用一對(duì)多的關(guān)系把它分解為雙向關(guān)系)
MyBatis中3種級(jí)聯(lián)
  • association钱慢,代表一對(duì)一關(guān)系逮京,比如每個(gè)班級(jí)都有一個(gè)班長
  • collection,代表一對(duì)多關(guān)系束莫,比如每個(gè)班級(jí)有多個(gè)學(xué)生
  • discriminator懒棉,鑒別器草描,它可以根據(jù)實(shí)際選擇采用哪個(gè)類作為實(shí)例,允許你根據(jù)特定的條件去關(guān)聯(lián)不同的結(jié)果集策严。
association與collection

現(xiàn)在有兩張表分別是班級(jí)表和學(xué)生表穗慕,我們?cè)诎嗉?jí)表的Mapper文件中,通過association映射了班級(jí)和班長一對(duì)一的關(guān)系妻导;通過collection映射了班級(jí)和學(xué)生的一對(duì)多關(guān)系逛绵。此時(shí)我們通過ClassMapper的select方法,能夠自動(dòng)裝填對(duì)應(yīng)的班長和學(xué)生信息倔韭。

CREATE TABLE `class` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '',
  `grade` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

CREATE TABLE `student` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL DEFAULT '',
  `sex` int(11) NOT NULL DEFAULT '0',
  `class_id` bigint(20) NOT NULL DEFAULT '0',
  `monitor` int(11) NOT NULL DEFAULT '0' COMMENT '0不是班長术浪,1是班長',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
<!-- 這是班級(jí)表 -->
<mapper namespace="com.ankeetc.spring.mapper.ClassMapper" >
    <resultMap id="base_result_map" type="com.ankeetc.spring.domain.ClassDomain">
        <id property="id" column="id" javaType="Long" jdbcType="BIGINT" />
        <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
        <!-- 班級(jí)與班長的一對(duì)一關(guān)系 -->
        <association property="classMonitor" column="id" select="com.ankeetc.spring.mapper.StudentMapper.selectClassMonitor"/>
        <!-- 班級(jí)與學(xué)生的一對(duì)一關(guān)系 -->
        <collection property="students" column="id" select="com.ankeetc.spring.mapper.StudentMapper.selectByClassId"/>
    </resultMap>

    <select id="select" resultMap="base_result_map">
        SELECT * FROM class WHERE id = #{id}
    </select>
</mapper>
<!-- 這是學(xué)生表 -->
<mapper namespace="com.ankeetc.spring.mapper.StudentMapper" >
    <resultMap id="base_result_map" type="com.ankeetc.spring.domain.StudentDomain">
        <id property="id" column="id" javaType="Long" jdbcType="BIGINT" />
        <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
        <result property="sex" column="sex" javaType="Integer" jdbcType="INTEGER"/>
        <result property="classId" column="class_id" javaType="Long" jdbcType="BIGINT"/>
        <result property="monitor" column="monitor" javaType="Integer" jdbcType="INTEGER"/>
    </resultMap>

    <select id="selectByClassId" resultMap="base_result_map">
        SELECT * FROM student WHERE class_id = #{classId}
    </select>

    <select id="selectClassMonitor" resultMap="base_result_map">
        SELECT * FROM student WHERE class_id = #{classId} AND monitor = 1 LIMIT 1
    </select>
</mapper>
discriminator鑒別器

鑒別器級(jí)聯(lián)是在特定的條件下去使用不同的POJO,比如在上例中寿酌,我們可以根據(jù)班級(jí)中的年級(jí)屬性(grade)進(jìn)行判斷去關(guān)聯(lián)是小學(xué)還是初中胰苏,然后分別進(jìn)行不同的映射。discriminator相當(dāng)于Java語言中的switch語句醇疼。

<mapper namespace="com.ankeetc.spring.mapper.ClassMapper" >

    <resultMap id="base_result_map" type="com.ankeetc.spring.domain.ClassDomain">
        <id property="id" column="id" javaType="Long" jdbcType="BIGINT" />
        <result property="name" column="name" javaType="String" jdbcType="VARCHAR"/>
        <association property="classMonitor" column="id" select="com.ankeetc.spring.mapper.StudentMapper.selectClassMonitor"/>
        <collection property="students" column="id" select="com.ankeetc.spring.mapper.StudentMapper.selectByClassId"/>
        <!-- 通過discriminator進(jìn)行分配 -->
        <discriminator javaType="Integer" column="grade">
            <case value="1" resultMap="primary"/>
            <case value="2" resultMap="junior_high"/>
        </discriminator>
    </resultMap>
    
    <!-- 如果grade=1則會(huì)使用這個(gè)resultMap硕并,返回的實(shí)際類型是PrimaryClassDomain -->
    <resultMap id="primary" type="com.ankeetc.spring.domain.PrimaryClassDomain" extends="base_result_map">
        <result property="grade" column="grade" javaType="Integer" jdbcType="INTEGER"/>
    </resultMap>
    
    <!-- 如果grade=2則會(huì)使用這個(gè)resultMap,返回的實(shí)際類型是PrimaryClassDomain -->
    <resultMap id="junior_high" type="com.ankeetc.spring.domain.PrimaryClassDomain" extends="base_result_map">
        <result property="grade" column="grade" javaType="Integer" jdbcType="INTEGER"/>
    </resultMap>

    <select id="select" resultMap="base_result_map">
        SELECT * FROM class WHERE id = #{id}
    </select>
</mapper>
級(jí)聯(lián)的優(yōu)缺點(diǎn)

級(jí)聯(lián)的優(yōu)勢(shì):能夠方便快捷地獲取數(shù)據(jù)秧荆。
級(jí)聯(lián)的缺點(diǎn):有時(shí)候我們并不需要獲取所有的數(shù)據(jù)倔毙;有一個(gè)關(guān)聯(lián)我們就要多執(zhí)行一次SQL,這樣會(huì)造成SQL執(zhí)行過多導(dǎo)致性能下降辰如,這就是N+1的問題普监,為了解決這個(gè)問題我們應(yīng)該考慮采用延遲加載的功能。

全局延遲加載

為了處理N+1的問題琉兜,My Batis引入了延遲加載的功能凯正。延遲加載功能的意義在于,開始并不取出級(jí)聯(lián)數(shù)據(jù)豌蟋,只有當(dāng)使用它了才發(fā)送SQL去取回?cái)?shù)據(jù)廊散。MyBatis的配置中有兩個(gè)全局的參數(shù)lazyLoadingEnabled和aggressiveLazyLoading。

  • lazyLoadingEnabled的含義是是否開啟延遲加載功能梧疲;
  • aggressiveLazyLoading的含義是對(duì)任意延遲屬性的調(diào)用會(huì)使帶有延遲加載屬性的對(duì)象完整加載允睹;反之,每種屬性將按需加載幌氮。
局部延遲加載

由于全局配置的靈活性較差缭受,MyBatis提供了局部延遲加載的功能。我們可以在association和 collection元素上加入屬性值fetchType就可以了该互。它有兩個(gè)取值范圍米者,eager和lazy。它的默認(rèn)值取決于你在配置文件settings的配置。假如我們沒有配置它蔓搞,那么它們就是eager一旦你配置了它們胰丁,那么全局的變量就會(huì)被它們所覆蓋,這樣我們就可以靈活地指定哪些東西可以立即加載喂分,哪些東西可以延遲加載锦庸。

9 緩存cache

目前流行的緩存服務(wù)器有MongoDB、Redis蒲祈、Ehcache等甘萧,緩存將數(shù)據(jù)保存在內(nèi)存中,在讀取的時(shí)候無需再從磁盤讀入梆掸,因此具備快速讀取和使用的特點(diǎn)幔嗦。如果緩存命中率高,那么可以極大地提高系統(tǒng)的性能沥潭;如果緩存命中率很低,那么緩存就不存在使用的意義了嬉挡,所以使用緩存的關(guān)鍵就是提高命中钝鸽。

9.1 一級(jí)系統(tǒng)緩存

  • MyBatis對(duì)緩存提供支持,在沒有配置的默認(rèn)的情況下庞钢,它只開啟一級(jí)緩存(一級(jí)緩存只是相對(duì)于同一個(gè) SqlSession而言)拔恰,所以在參數(shù)和SQL完全一樣的情況下,我們使用同一個(gè) SqlSession對(duì)象調(diào)用同一個(gè)Mapper的方法基括,往往只執(zhí)行一次SQL颜懊。
  • 使用SqlSession第一次查詢后,MyBatis會(huì)將其放在緩存中风皿。以后再查詢的時(shí)候河爹,如果沒有聲明需要刷新且緩存沒超時(shí),SqlSession都只會(huì)取出當(dāng)前緩存的數(shù)據(jù)桐款,而不會(huì)再次發(fā)送SQL到數(shù)據(jù)庫咸这。
  • 如果使用的是不同的SqlSession對(duì)象,因?yàn)椴煌腟qlSession都是相互隔離的魔眨,所以用相同的 Mapper媳维、參數(shù)和方法,它還是會(huì)再次發(fā)送SQL到數(shù)據(jù)庫去執(zhí)行遏暴。
    public static void func() throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
            // 在此處執(zhí)行了兩次select操作侄刽,只有依次數(shù)據(jù)庫訪問操作
            studentMapper.selectClassMonitor(1L);
            studentMapper.selectClassMonitor(1L);

            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
==>  Preparing: SELECT * FROM student WHERE class_id = ? AND monitor = 1 LIMIT 1 
==> Parameters: 1(Long)
<==    Columns: id, name, sex, class_id, monitor
<==        Row: 2, lucas, 20, 1, 1
<==      Total: 1
    <!--使用標(biāo)準(zhǔn)log來實(shí)現(xiàn)日志打印-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

9.2 二級(jí)系統(tǒng)緩存

一級(jí)緩存在各個(gè)SqlSession間是相互隔離的。為了克服這個(gè)問題朋凉,我們往往需要配置二級(jí)緩存州丹,使得緩存在SqlsessionFactory層面上能夠提供給各個(gè)SqISession對(duì)象共享,二級(jí)緩存默認(rèn)是不開啟的侥啤。

  1. 二級(jí)緩存的時(shí)候要求返回的POJO必須是可序列化的,也就是要求實(shí)現(xiàn)Serializable接口当叭。
  2. 配置的緩存的方法很簡單茬故,只需要在Mapper.xml文件中加入<cache/>磺芭。
    public static void func() throws Exception {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
        try (SqlSession sqlSession1 = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession()) {
            StudentMapper studentMapper1 = sqlSession1.getMapper(StudentMapper.class);
            StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);

            // 如果沒有<cache/>標(biāo)簽,這條語句將會(huì)執(zhí)行兩次select操作
            studentMapper1.selectClassMonitor(1L);
            studentMapper2.selectClassMonitor(1L);

            sqlSession1.commit();
            sqlSession2.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
cache的默認(rèn)配置
  • 映射語句文件中的所有select語句將會(huì)被緩存钾腺。
  • 映射語句文件中的所有insert讥裤、update和delete語句會(huì)刷新緩存。
  • 緩存會(huì)使用默認(rèn)的Least Recently Used(LRU己英,最近最少使用的)算法來收回。
  • 根據(jù)時(shí)間表损肛,比如No Flush Interval,(NFI治拿,沒有刷新間隔),緩存不會(huì)以任何時(shí)
    間順序來刷新
  • 緩存會(huì)存儲(chǔ)列表集合或?qū)ο螅o論查詢方法返回什么)的1024個(gè)引用见坑。
  • 緩存會(huì)被視為是read/write(可讀/可寫)的緩存,意味著對(duì)象檢索不是共享的捏检,且可以安全地被調(diào)用者修改荞驴,不干擾其他調(diào)用者或線程所做的潛在修改。
配置緩存屬性
  • eviction:代表的是緩存回收策略贯城,LRU(最近最少使用的戴尸,移除最長時(shí)間不用的對(duì)象)、FFO(先進(jìn)先出冤狡,按對(duì)象進(jìn)入緩存的順序來移除它們)孙蒙、SOFT(軟引用,移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對(duì)象)悲雳、WEAK(弱引用挎峦,更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對(duì)象)。
  • flushInterval:刷新間隔時(shí)間合瓢,單位為亳秒,這里配置的是100秒刷新坦胶,如果不配置它,那么當(dāng)SQL被執(zhí)行的時(shí)候才會(huì)去刷新緩存。
  • size:引用數(shù)目顿苇,一個(gè)正整數(shù)峭咒,代表緩存最多可以存儲(chǔ)多少個(gè)對(duì)象,設(shè)置過大會(huì)導(dǎo)致內(nèi)存溢出纪岁。
  • readOnly:只讀凑队,意味著緩存數(shù)據(jù)只能讀取而不能修改,這樣設(shè)置的好處是我們可以快速讀取緩存幔翰,缺點(diǎn)是我們沒有辦法修改緩存漩氨,默認(rèn)不允許我們修改。

9.3 自定義緩存

系統(tǒng)緩存是MyBatis應(yīng)用機(jī)器上的本地緩存遗增,但是在分布式系統(tǒng)上叫惊,需要使用各類不同的緩存服務(wù)器,這是需要實(shí)現(xiàn)MyBatis為我們提供的接口org.apache.ibatis.cache.Cache做修,緩存接口簡介如下所示:

  // 獲取緩存編號(hào)
  String getId();
  // 保存key value
  void putObject(Object key, Object value);
  // 根據(jù)key獲取value
  Object getObject(Object key);
  // 根據(jù)key移除對(duì)象
  Object removeObject(Object key);
  // 清空緩存
  void clear();
  // 獲取緩存大小
  int getSize();
  // 獲取緩存對(duì)讀寫鎖
  ReadWriteLock getReadWriteLock();

我們?cè)谟成淦魃系膇nsert霍狰、delete、select饰及、update等元素蚓耽,可以配置useCache和 flushCache兩個(gè)屬性來控制緩存的使用旋炒,useCache表示是否需要使用緩存瘫镇,而flushCache表示插入后是否需要刷新緩存铣除。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末尚粘,一起剝皮案震驚了整個(gè)濱河市敲长,隨后出現(xiàn)的幾起案子祈噪,更是在濱河造成了極大的恐慌辑鲤,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓢喉,死亡現(xiàn)場離奇詭異栓票,居然都是意外死亡逗载,警方通過查閱死者的電腦和手機(jī)厉斟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門擦秽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來感挥,“玉大人越败,你說我怎么就攤上這事究飞∫诟担” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長酬滤。 經(jīng)常有香客問我盯串,道長,這世上最難降的妖魔是什么男摧? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任耗拓,我火速辦了婚禮乔询,結(jié)果婚禮上竿刁,老公的妹妹穿的比我還像新娘。我一直安慰自己鸵熟,他們只是感情好流强,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布打月。 她就那樣靜靜地躺著奏篙,像睡著了一般秘通。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梗脾,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天炸茧,我揣著相機(jī)與錄音梭冠,去河邊找鬼控漠。 笑死悬钳,一個(gè)胖子當(dāng)著我的面吹牛默勾,可吹牛的內(nèi)容都是我干的母剥。 我是一名探鬼主播环疼,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼炫隶,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼等限!你這毒婦竟也來了望门?” 一聲冷哼從身側(cè)響起锰霜,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤厨剪,失蹤者是張志新(化名)和其女友劉穎友存,沒想到半個(gè)月后屡立,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膨俐,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡焚刺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年乳愉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拖叙。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡咖气,死狀恐怖挖滤,靈堂內(nèi)的尸體忽然破棺而出斩松,到底是詐尸還是另有隱情,我是刑警寧澤乳幸,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布粹断,位于F島的核電站瓶埋,受9級(jí)特大地震影響养筒,放射性物質(zhì)發(fā)生泄漏端姚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一巫湘、第九天 我趴在偏房一處隱蔽的房頂上張望剩膘。 院中可真熱鬧盆顾,春花似錦您宪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怠晴。三九已至蒜田,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間美莫,已是汗流浹背厢呵。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工述吸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蝌矛,地道東北人错英。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓椭岩,卻偏偏與公主長得像判哥,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挺身,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容