Mybatis系列全解(五):全網(wǎng)最全居兆!詳解Mybatis的Mapper映射文件

封面:洛小汐
作者:潘潘

若不是生活所迫覆山,誰愿意背負(fù)一身才華。

前言

上節(jié)我們介紹了 《 Mybatis系列全解(四):全網(wǎng)最全泥栖!Mybatis配置文件 XML 全貌詳解 》簇宽,內(nèi)容很詳細(xì)( 也很枯燥),由于篇幅實(shí)在過于冗長聊倔,我預(yù)計(jì)大家想看完得花上兩段上班地鐵公交車的時(shí)間 。生巡。耙蔑。

不過應(yīng)該有讓大家了解到 Mybatis 的核心配置文件 config.xml 全貌,其中的 <mappers></mappers> 元素即是我們本節(jié)準(zhǔn)備登場介紹的 SQL 映射器孤荣,上節(jié)有介紹了三種引入 SQL 映射器的方式甸陌,本節(jié)我們就主要聊聊它的幾個(gè)頂級(jí)元素用法。

Mybatis 真正強(qiáng)大就在于它的語句映射盐股,這是它的魔力所在钱豁,也是基石。由于它異常強(qiáng)大疯汁,映射器的 XML 文件就顯得相對(duì)簡單牲尺。如果拿它跟具有相同功能的 JDBC 代碼進(jìn)行對(duì)比,你會(huì)立即發(fā)現(xiàn)省掉了將近 95% 的代碼( 95% 是Mybatis 官網(wǎng)的說法 ,我也就引入一下 )谤碳,MyBatis 致力于減少使用成本溃卡,讓用戶能更專注于 SQL 代碼。

Mybatis系列全解腦圖分享蜒简,持續(xù)更新中

Mybaits系列全解 (持續(xù)更新)


目錄


1犹赖、mapper 映射器頂級(jí)元素全貌

2、namespace 命名空間

3卷仑、select 查詢

4峻村、insert / update / delete 增刪改

5、cache 緩存

6系枪、cache-ref 緩存引用

7雀哨、sql 語句塊

8、parameterMap 參數(shù)映射

9私爷、總結(jié)

mapper 映射器頂級(jí)元素全貌

與其它 ORM 框架如 Hibernate 不同雾棺,Mybatis 的框架思想希望開發(fā)者能夠直接操作數(shù)據(jù)庫編寫 SQL,而不是隱藏起來衬浑,讓開發(fā)者獨(dú)自面對(duì) Java 對(duì)象捌浩,為此 Mybatis 設(shè)計(jì)了 SQL 映射器,任你五招十二式工秩。

映射器有九大頂級(jí)元素 尸饺,基本技能介紹

  • select : 用于查詢,支持傳參助币,返回指定結(jié)果集浪听;
  • insert : 用于新增,支持傳參眉菱,返回指定結(jié)果集迹栓;
  • update : 用于更新,支持傳參俭缓,返回指定結(jié)果集克伊;
  • delete : 用于刪除,支持傳參华坦,返回指定結(jié)果集愿吹;
  • sql : 被其它語句引用的 可復(fù)用 語句塊;
  • cache : 當(dāng)前命名空間緩存配置惜姐;
  • cache-ref : 引用其它命名空間的緩存配置犁跪;
  • parameterMap : 參數(shù)映射,已棄用,是它不夠好耘拇;
  • resultMap : 結(jié)果集映射撵颊,它就很好;

其中惫叛,增刪改查操作拼接 SQL 時(shí)使用到的 動(dòng)態(tài)SQL( if倡勇、where、foreach啥的)嘉涌,以及封裝結(jié)果集時(shí)使用到的 復(fù)雜映射 (1對(duì)1 妻熊,1對(duì)多,多對(duì)多啥的)仑最,這兩部分我們后面單立文章再詳細(xì)介紹扔役,本文中我們簡單點(diǎn)過。

九大頂級(jí)元素 警医,功能歸類:

其中頂一元素 parameterMap 已建議棄用了 亿胸。

無論你有多么復(fù)雜的 SQL 操作,最根本的思路都逃不出以上 4 部分预皇。

namespace 命名空間

一個(gè)完整的 Mapper 映射文件侈玄,需要有約束頭 xml 與 !DOCTYPE ,其次才是 mapper 根元素吟温,最后再是頂級(jí)元素序仙,而其中,namespace 屬性作為 mapper 的唯一標(biāo)識(shí)鲁豪,試回憶:

  • 上學(xué)時(shí)潘悼,6年級(jí)一班23號(hào),能代表唯一的你爬橡。
  • 編寫 Java 類時(shí)治唤,包名 + 類名,能代表唯一的類糙申。
  • 而如今宾添,我們?cè)?Mybatis 中寫的每一段 SQL 語句,同樣有唯一的代表方式郭宝,那就是「 命名空間標(biāo)識(shí) + 語句id 」辞槐,無論是為了區(qū)分業(yè)務(wù)也好掷漱,還是為了拆分服務(wù)也好粘室,反正 Mybatis 讓每一個(gè) mapper.xml 配備一個(gè)唯一命名空間標(biāo)識(shí)。

每一段 SQL 語句都是唯一定義的卜范,我們?cè)?Mybatis 中用「 命名空間標(biāo)識(shí) + 語句塊 ID 」作為唯一的標(biāo)識(shí)衔统,組合之后在 Mybatis 二級(jí)緩存中可以作為本地 map 集合 緩存 的唯一Key ,也可以用于 Dao 接口的 映射 綁定,還能作為唯一 代理 標(biāo)識(shí)锦爵〔盏睿總之,我們希望避免命名沖突和重復(fù)定義险掀,所以沪袭,擁有這么一個(gè)唯一標(biāo)識(shí) ,它就至少有一億個(gè)利好樟氢。

select 查詢

select 查詢語句冈绊,幾乎是我們最高頻的使用元素,所以 Mybatis 在這塊沒少下功夫埠啃,目的就是通過提供盡可能多的便利死宣,讓我們的查詢操作變得簡單。 一個(gè)查詢用戶 User 的查詢語句可以這么編寫:

<select id="selectUser" parameterType="int" resultType="hashmap">
  select * from t_user where id = #{id}
</select>
  • id屬性:在當(dāng)前 mapper.xml 命名空間下碴开,它的 id 值是唯一的( 不過如果在不同的 mapper.xml 命名空間下毅该,則允許有相同的的 id 值 )
  • parameterType 屬性:代表傳入的參數(shù)類型,這里是 int (或 Integer)類型
  • resultType屬性:代表返回結(jié)果類型潦牛,這里指定返回一個(gè) hashMap 類型的對(duì)象眶掌,mybatis 會(huì)把查詢出來的數(shù)據(jù)表記錄對(duì)應(yīng)的 ' 字段列名 - 字段值 ',自動(dòng)映射為 map 集合的 key - value 罢绽。

當(dāng)然如果你不希望通過 hashmap 來接收查詢結(jié)果畏线,允許你自由指定返回類型。Mybatis 是支持自動(dòng)綁定 JavaBean 的良价,我們只要讓查詢返回的字段名和 JavaBean 的屬性名保持一致(或者采用駝峰式命名)寝殴,便可以自動(dòng)映射結(jié)果集,例如你創(chuàng)建一個(gè) Java 類 User.java 明垢,包含兩個(gè)屬性 id 和 name , 那么結(jié)果集可以指定為 com.vo.User 蚣常,就完成了。

<select id="selectUser" 
    parameterType="int" resultType="com.vo.User">
  select * from t_user where id = #{id}
</select>

注意參數(shù)符號(hào):

#{id}

'#{}' 告訴 MyBatis 創(chuàng)建一個(gè)預(yù)編譯語句(PreparedStatement)參數(shù)痊银,在 JDBC 中抵蚊,這樣的一個(gè)參數(shù)在 SQL 中會(huì)由一個(gè) “ ? ” 來標(biāo)識(shí),并被傳遞到一個(gè)新的預(yù)編譯語句中溯革,就像這樣:

// 近似的 JDBC 代碼贞绳,非 MyBatis 代碼...
String selectUser = " select * from t_user where id = ? ";
PreparedStatement ps = conn.prepareStatement(selectUser);
ps.setInt(1,id);

'#{}' 作為占位符,${} 作為替換符致稀,兩者沒有孰輕孰重冈闭,只不過應(yīng)用場景不同,適當(dāng)取舍即可抖单。

我們希望完成類似 JDBC 中的 PrepareStatement 預(yù)編譯處理 萎攒,可以使用 #{} 遇八,它會(huì)在替換占位符時(shí)首尾添加上單引號(hào) '' ,能有效防止 SQL 注入 風(fēng)險(xiǎn)耍休。

例如使用 ${} 操作刪除 ( 就很有問題H杏馈)

// 1、使用 ${} 有注入風(fēng)險(xiǎn)
delete from t_user where id = ${id}

// 2羊精、正常傳值斯够,id 傳入 1  
delete from t_user where id = 1
// 結(jié)果刪除了id=1 的記錄
    
// 3、注入風(fēng)險(xiǎn)喧锦,id 傳入 1 or 1=1 
delete from t_user where id = 1 or 1=1
// 全表刪除了

再看看 #{} 是如何規(guī)避 SQL 注入 的:

// 1雳刺、使用 #{} 有效防止注入風(fēng)險(xiǎn)
delete from t_user where id = #{id}

// 2、正常傳值裸违,id 傳入 1   
delete from t_user where id = '1'
// 結(jié)果刪除了id=1 的記錄
    
// 3掖桦、注入風(fēng)險(xiǎn),id 傳入 1 or 1=1 
delete from t_user where id = '1 or 1=1'
// SQL 語句報(bào)錯(cuò)供汛,表數(shù)據(jù)安全

雖然在防止 SQL 注入方面枪汪,{} 確實(shí)無能為力,不過我們{} 在其它方面可不容小覷怔昨,例如它允許你靈活地進(jìn)行 動(dòng)態(tài)表和動(dòng)態(tài)列名的替換 操作雀久,例如:

// 1、靈活查詢指定表數(shù)據(jù)
select * from ${tableName} 

// 傳入 tableName參數(shù) = t_user , 結(jié)果
select * from t_user  

// 2趁舀、靈活查詢不同列條件數(shù)據(jù)
select * from t_user where ${colunmName} = ${value}

// 傳入 colunmName參數(shù) = name , value參數(shù) = '潘潘', 結(jié)果
select * from t_user where name = '潘潘'

// 傳入 colunmName參數(shù) = id , value參數(shù) = 1, 結(jié)果
select * from t_user where id = 1

以上的 {} 替換列名與表名的方式非常靈活赖捌,不過確實(shí)存在 SQL 注入風(fēng)險(xiǎn),所以在考慮使用 #{} 或{} 前矮烹,需要評(píng)估風(fēng)險(xiǎn)越庇,避免風(fēng)險(xiǎn),允許的情況下奉狈,我建議使用 #{} 卤唉。

當(dāng)然,select 元素允許你配置很多屬性來配置每條語句的行為細(xì)節(jié)仁期。

<select     
  id="selectUser" 
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY"
  databaseId="mysql"
  resultOrdered="false"
  resultSets="rs1,rs2,rs3">
  select * from t_user
</select>

下面詳細(xì)介紹一下桑驱,略微冗長,一口氣看完吧:

  • id 必填項(xiàng)跛蛋,在命名空間下的唯一標(biāo)識(shí)熬的,可被 Mybatis 引用,如果存在相同的 “ 命名空間 + 語句id ” 組合赊级,Mybatis 將拋出異常押框;
  • parameterType 可選項(xiàng),傳入語句的參數(shù)的類全限定名或別名此衅,可以是基本類型强戴、map 或 JavaBean 等復(fù)雜的參數(shù)類型傳遞給 SQL;
  • parameterMap 用于引用外部 parameterMap 的屬性塊挡鞍,目前已被廢棄骑歹。以后請(qǐng)使用行內(nèi)參數(shù)映射和 parameterType 屬性。
  • resultType 可選項(xiàng)墨微,定義類的全路徑道媚,在允許自動(dòng)匹配的情況下,結(jié)果集將通過 Javaben 的規(guī)范映射翘县,或定義為 int 最域、double、float 等參數(shù)锈麸;也可以使用別名镀脂,但是要符合別名規(guī)范和定義。 resultType 和 resultMap 之間只能同時(shí)使用一個(gè)忘伞。(日常中薄翅,比如我們統(tǒng)計(jì)結(jié)果總條數(shù)的時(shí)候可以設(shè)置為 int );
  • resultMap 可選項(xiàng)氓奈,對(duì)外部 resultMap 的命名引用翘魄。結(jié)果映射是 MyBatis 最強(qiáng)大的特性,如果你對(duì)其理解透徹舀奶,許多復(fù)雜的映射問題都能迎刃而解暑竟,后面一對(duì)一、一對(duì)多育勺、多對(duì)多我們會(huì)有一篇文章單獨(dú)講解但荤。 resultType 和 resultMap 之間只能同時(shí)使用一個(gè)。
  • flushCache 可選項(xiàng)涧至,清空緩存纱兑,將其設(shè)置為 true 后,只要語句被調(diào)用化借,都會(huì)導(dǎo)致本地緩存和二級(jí)緩存被清空潜慎,默認(rèn)值:false。
  • useCache 可選項(xiàng)蓖康,使用緩存铐炫,將其設(shè)置為 true 后,將會(huì)導(dǎo)致本條語句的結(jié)果被二級(jí)緩存緩存起來蒜焊,默認(rèn)值:對(duì) select 元素為 true倒信。
  • timeout 可選項(xiàng),這個(gè)設(shè)置是在拋出異常之前泳梆,驅(qū)動(dòng)程序等待數(shù)據(jù)庫返回請(qǐng)求結(jié)果的秒數(shù)鳖悠。默認(rèn)值為未設(shè)置(unset)(依賴數(shù)據(jù)庫驅(qū)動(dòng))榜掌。
  • fetchSize 可選項(xiàng),獲取記錄的總條數(shù)設(shè)定乘综。這是一個(gè)給驅(qū)動(dòng)的建議值憎账,嘗試讓驅(qū)動(dòng)程序每次批量返回的結(jié)果行數(shù)等于這個(gè)設(shè)置值。 默認(rèn)值為未設(shè)置(unset)(依賴驅(qū)動(dòng))卡辰。由于性能問題胞皱,建議在 sql 做分頁處理。
  • statementType 可選項(xiàng)九妈,可選 STATEMENT反砌,PREPARED 或 CALLABLE。這會(huì)讓 MyBatis 分別使用 Statement萌朱,PreparedStatement 或 CallableStatement宴树,默認(rèn)值:PREPARED。
  • resultSetType 可選項(xiàng)晶疼,F(xiàn)ORWARD_ONLY森渐,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等價(jià)于 unset) 中的一個(gè),默認(rèn)值為 unset (依賴數(shù)據(jù)庫驅(qū)動(dòng))冒晰。
- FORWARD_ONLY同衣,只允許游標(biāo)向前訪問;
- SCROLL_SENSITIVE壶运,允許游標(biāo)雙向滾動(dòng)耐齐,但不會(huì)及時(shí)更新數(shù)據(jù),也就是說如果數(shù)據(jù)庫中的數(shù)據(jù)被修改過蒋情,并不會(huì)在resultSet中及時(shí)更新出來埠况;
- SCROLL_INSENSITIVE ,允許游標(biāo)雙向滾動(dòng)棵癣,如果數(shù)據(jù)庫中的數(shù)據(jù)被修改過辕翰,會(huì)及時(shí)更新到resultSet;

我們知道 JDBC 通過 ResultSet 來對(duì)查詢結(jié)果進(jìn)行封裝狈谊,ResultSet 對(duì)象本身包含了一個(gè)由查詢語句返回的一個(gè)結(jié)果集合喜命。例如你經(jīng)常在 JDBC 見過的結(jié)果集讀取:

// 允許滾動(dòng)游標(biāo)索引結(jié)果集
while( rs.next() ){
    rs.getString("name");
}
// 當(dāng)然也支持游標(biāo)定位到最后一個(gè)位置
rs.last();
// 向后滾動(dòng)
rs.previous();
  • databaseId 可選項(xiàng)河劝,如果配置了數(shù)據(jù)庫廠商標(biāo)識(shí)(databaseIdProvider)壁榕,MyBatis 會(huì)加載所有不帶 databaseId 或匹配當(dāng)前 databaseId 的語句;如果帶和不帶的語句都有赎瞎,則不帶的會(huì)被忽略牌里。
  • resultOrdered 可選項(xiàng),這個(gè)設(shè)置僅針對(duì)嵌套結(jié)果 select 語句:如果為 true务甥,將會(huì)假設(shè)包含了嵌套結(jié)果集或是分組牡辽,當(dāng)返回一個(gè)主結(jié)果行時(shí)喳篇,就不會(huì)產(chǎn)生對(duì)前面結(jié)果集的引用。 這就使得在獲取嵌套結(jié)果集的時(shí)候不至于內(nèi)存不夠用态辛。默認(rèn)值:false麸澜。
  • resultSets 這個(gè)設(shè)置僅適用于多結(jié)果集的情況。它將列出語句執(zhí)行后返回的結(jié)果集并賦予每個(gè)結(jié)果集一個(gè)名稱因妙,多個(gè)名稱之間以逗號(hào)分隔。

insert / update / delete 增刪改

數(shù)據(jù)變更語句 insert票髓,update 和 delete 的實(shí)現(xiàn)非常接近攀涵,而且相對(duì)于 select 元素而言要簡單許多。

<insert
  id="insertUser"
  parameterType="domain.vo.User"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="updateUser"
  parameterType="domain.vo.User"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteUser"
  parameterType="domain.vo.User"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

其中大部分屬性和 select 元素相同洽沟,我們介紹 3 個(gè)不同的屬性:

  • useGeneratedKeys : (僅適用于 insert 和 update)這會(huì)令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由數(shù)據(jù)庫內(nèi)部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)的自動(dòng)遞增字段)以故,默認(rèn)值:false。
  • keyProperty : (僅適用于 insert 和 update)指定能夠唯一識(shí)別對(duì)象的屬性裆操,MyBatis 會(huì)使用 getGeneratedKeys 的返回值或 insert 語句的 selectKey 子元素設(shè)置它的值怒详,默認(rèn)值:未設(shè)置(unset)。如果生成列不止一個(gè)踪区,可以用逗號(hào)分隔多個(gè)屬性名稱昆烁。
  • keyColumn : (僅適用于 insert 和 update)設(shè)置生成鍵值在表中的列名,在某些數(shù)據(jù)庫(像 PostgreSQL)中缎岗,當(dāng)主鍵列不是表中的第一列的時(shí)候静尼,是必須設(shè)置的。如果生成列不止一個(gè)传泊,可以用逗號(hào)分隔多個(gè)屬性名稱鼠渺。

我們先看看 insert,update 和 delete 語句的示例:

<insert id="insertUser">
  insert into t_user (id,name) 
  values (#{id},#{name})
</insert>

<update id="updateUser">
  update t_user set name = #{name} where id = #{id}
</update>

<delete id="deleteUser">
  delete from t_user where id = #{id}
</delete>

如前所述眷细,插入語句的配置規(guī)則更加豐富拦盹,在插入語句里面有一些額外的屬性和子元素用來處理主鍵的生成,并且提供了多種生成方式溪椎。

首先普舆,如果你的數(shù)據(jù)庫支持 自動(dòng)生成主鍵 的字段(比如 MySQL 和 SQL Server),那么你可以設(shè)置 useGeneratedKeys=”true”校读,然后再把 keyProperty 設(shè)置為目標(biāo)屬性就 OK 了奔害。例如,如果上面的 t_user 表已經(jīng)在 id 列上使用了自動(dòng)生成地熄,那么語句可以修改為:

<insert id="insertUser" useGeneratedKeys="true"
    keyProperty="id">
  insert into t_user (name) values (#{name})
</insert>

如果你的數(shù)據(jù)庫還支持多行插入, 你也可以傳入一個(gè) User 數(shù)組或集合华临,并返回自動(dòng)生成的主鍵。

<insert id="insertUser" useGeneratedKeys="true"
    keyProperty="id">
  
  insert into t_user (name) values  
    
  <foreach item="item" collection="list" separator=",">
    (#{item.name})
  </foreach>
</insert>

對(duì)于不支持自動(dòng)生成主鍵列的數(shù)據(jù)庫和可能不支持自動(dòng)生成主鍵的 JDBC 驅(qū)動(dòng)端考,MyBatis 有另外一種方法來生成主鍵雅潭。

這里有一個(gè)簡單(也很傻)的示例揭厚,它可以生成一個(gè)隨機(jī) ID(不建議實(shí)際使用,這里只是為了展示 MyBatis 處理問題的靈活性和寬容度):

<insert id="insertUser">
 
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
    
  insert into t_user (id, name)
  values  (#{id}, #{name})
</insert>

在上面的示例中扶供,首先會(huì)運(yùn)行 selectKey 元素中的語句筛圆,并設(shè)置 User 的 id,然后才會(huì)調(diào)用插入語句椿浓。這樣就實(shí)現(xiàn)了數(shù)據(jù)庫自動(dòng)生成主鍵類似的行為太援,同時(shí)保持了 Java 代碼的簡潔。

selectKey 元素描述如下:

<selectKey
  keyProperty="id"
  resultType="int"
  order="BEFORE"
  statementType="PREPARED">

selectKey 中的 order 屬性有2個(gè)選擇:BEFORE 和 AFTER 扳碍。

  • BEFORE:表示先執(zhí)行selectKey的語句提岔,然后將查詢到的值設(shè)置到 JavaBean 對(duì)應(yīng)屬性上,然后再執(zhí)行 insert 語句笋敞。
  • AFTER:表示先執(zhí)行 AFTER 語句碱蒙,然后再執(zhí)行 selectKey 語句,并將 selectKey 得到的值設(shè)置到 JavaBean 中的屬性夯巷。上面示例中如果改成 AFTER赛惩,那么插入的 id 就會(huì)是空值,但是返回的 JavaBean 屬性內(nèi)會(huì)有值趁餐。

cache 緩存

緩存對(duì)于互聯(lián)網(wǎng)系統(tǒng)來說特別常見喷兼,其特點(diǎn)就是將數(shù)據(jù)保存在內(nèi)存中。MyBatis 內(nèi)置了一個(gè)強(qiáng)大的事務(wù)性查詢緩存機(jī)制后雷,它可以非常方便地配置和定制褒搔。 為了使它更加強(qiáng)大而且易于配置绊汹,我們對(duì) MyBatis 3 中的緩存實(shí)現(xiàn)進(jìn)行了許多改進(jìn)奉件。

默認(rèn)情況下,只啟用了本地的會(huì)話緩存(即一級(jí)緩存钝的,sqlSession級(jí)別 )惧辈,它僅僅對(duì)一個(gè)會(huì)話中的數(shù)據(jù)進(jìn)行緩存琳状。 要啟用全局的二級(jí)緩存,首先在全局配置文件config.xml文件中加入如下代碼:

<!--開啟二級(jí)緩存--> 
<settings> 
    <setting name="cacheEnabled" value="true"/> </settings>

其次在UserMapper.xml文件中開啟緩存:

<!--開啟二級(jí)緩存--> 
<cache></cache>

基本上就是這樣盒齿。這個(gè)簡單語句的效果如下:

  • 映射語句文件中的所有 select 語句的結(jié)果將會(huì)被緩存念逞。
  • 映射語句文件中的所有 insert、update 和 delete 語句會(huì)刷新緩存边翁。
  • 緩存會(huì)使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存翎承。
  • 緩存不會(huì)定時(shí)進(jìn)行刷新(也就是說,沒有刷新間隔)符匾。
  • 緩存會(huì)保存列表或?qū)ο螅o論查詢方法返回哪種)的 1024 個(gè)引用叨咖。
  • 緩存會(huì)被視為讀/寫緩存,這意味著獲取到的對(duì)象并不是共享的,可以安全地被調(diào)用者修改甸各,而不干擾其他調(diào)用者或線程所做的潛在修改垛贤。

緩存只作用于 cache 標(biāo)簽所在的映射文件中的語句。如果你混合使用 Java API 和 XML 映射文件趣倾,在共用接口中的語句將不會(huì)被默認(rèn)緩存聘惦。你需要使用 @CacheNamespaceRef 注解指定緩存作用域。

這些屬性可以通過 cache 元素的屬性來修改儒恋。比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

上面表示了一套更高級(jí)的緩存配置善绎,首先創(chuàng)建了一個(gè) FIFO 緩存,每隔 60 秒刷新诫尽,最多可以存儲(chǔ)結(jié)果對(duì)象或列表的 512 個(gè)引用禀酱,然后返回的對(duì)象被設(shè)置成只讀的,因此對(duì)它們進(jìn)行修改可能會(huì)在不同線程中的調(diào)用者產(chǎn)生沖突箱锐。

緩存可用的清除策略有:

  • LRU – 最近最少使用:移除最長時(shí)間不被使用的對(duì)象比勉。
  • FIFO – 先進(jìn)先出:按對(duì)象進(jìn)入緩存的順序來移除它們劳较。
  • SOFT – 軟引用:基于垃圾回收器狀態(tài)和軟引用規(guī)則移除對(duì)象驹止。
  • WEAK – 弱引用:更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對(duì)象。

默認(rèn)的清除策略是 LRU

flushInterval(刷新間隔)屬性可以被設(shè)置為任意的正整數(shù)观蜗,設(shè)置的值應(yīng)該是一個(gè)以毫秒為單位的合理時(shí)間量臊恋。 默認(rèn)情況是不設(shè)置,也就是沒有刷新間隔墓捻,緩存僅僅會(huì)在調(diào)用語句時(shí)刷新抖仅。

size(引用數(shù)目)屬性可以被設(shè)置為任意正整數(shù),要注意欲緩存對(duì)象的大小和運(yùn)行環(huán)境中可用的內(nèi)存資源砖第。默認(rèn)值是 1024撤卢。

readOnly(只讀)屬性可以被設(shè)置為 true 或 false。只讀的緩存會(huì)給所有調(diào)用者返回緩存對(duì)象的相同實(shí)例梧兼。 因此這些對(duì)象不能被修改放吩。這就提供了可觀的性能提升。而可讀寫的緩存會(huì)(通過序列化)返回緩存對(duì)象的拷貝羽杰。 速度上會(huì)慢一些渡紫,但是更安全,因此默認(rèn)值是 false考赛。

二級(jí)緩存是事務(wù)性的惕澎。這意味著,當(dāng) SqlSession 完成并提交 ( commit ) 時(shí)颜骤,或是完成并回滾 ( close ) 時(shí)唧喉,二級(jí)緩存都會(huì)被刷新。不管是否配置了 flushCache=true 。

Mybatis 的緩存包括一級(jí)緩存(sqlSession 級(jí)別)和二級(jí)緩存(mapper 級(jí)別)欣喧,所以 mapper 映射器中配置的是二級(jí)緩存腌零,我們先大概知道有這個(gè)概念,因?yàn)楹罄m(xù)我們會(huì)針對(duì)這兩種緩存進(jìn)行詳細(xì)介紹唆阿,而且還會(huì)講解如何自定義緩存益涧,因?yàn)?Mybatis 的緩存默認(rèn)都是以 map 的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)在本地,所以自定義緩存可以把存儲(chǔ)介質(zhì)拓展到磁盤或數(shù)據(jù)庫redis等驯鳖;而且一級(jí)緩存是默認(rèn)開啟的闲询,二級(jí)緩存需要我們手工開啟,這些后續(xù)都會(huì)詳細(xì)講解浅辙,提前預(yù)告扭弧。

緩存獲取順序:二級(jí)緩存 > 一級(jí)緩存 > 數(shù)據(jù)庫

cache-ref 引用緩存

回想一下 cache 的內(nèi)容,對(duì)某一命名空間的語句记舆,只會(huì)使用該命名空間的緩存進(jìn)行緩存或刷新鸽捻。 但你可能會(huì)想要在多個(gè)命名空間中共享相同的緩存配置和實(shí)例。要實(shí)現(xiàn)這種需求泽腮,你可以使用 cache-ref 元素來引用另一個(gè)緩存御蒲。

<cache-ref namespace="com.vo.UserMapper"/>

sql 語句塊

這個(gè)元素可以用來定義可重用的 SQL 代碼片段,以便在其它語句中使用诊赊。 參數(shù)可以靜態(tài)地(在加載的時(shí)候)確定下來厚满,并且可以在不同的 include 元素中定義不同的參數(shù)值。比如:

<sql id="userColumns"> 
    ${alias}.id,${alias}.name 
</sql>

這個(gè) SQL 片段可以在其它語句中使用碧磅,例如:

<select id="selectUsers" resultType="map">
  select
    <include refid="userColumns">
        <property name="alias" value="t1"/>
    </include>,
    <include refid="userColumns">
        <property name="alias" value="t2"/>
    </include>
  from t_user t1 cross join t_user t2
</select>

也可以在 include 元素的 refid 屬性或多層內(nèi)部語句中使用屬性值碘箍,例如:

<sql id="sql1">
  ${prefix}_user
</sql>

<sql id="sql2">
  from
    <include refid="${include_target}"/>
</sql>

<select id="select" resultType="map">
  select
    id, name
  <include refid="sql2">
    <property name="prefix" value="t"/>
    <property name="include_target" value="sql1"/>
  </include>
</select>

parameterMap 參數(shù)映射

parameterMap 元素官方已經(jīng)不建議使用,并且再后續(xù)版本會(huì)退出舞臺(tái)鲸郊。首先對(duì)于我們 Java 來說丰榴,特別不希望在代碼中通過傳遞 map 來傳參,這樣對(duì)于后續(xù)維護(hù)或者參數(shù)查找都是極不負(fù)責(zé)任的秆撮,我們推薦使用 JavaBean 來傳值參數(shù)四濒,這是 parameterMap 被拋棄的其中一個(gè)原因;另外也由于 parameterType 屬性的誕生就能很好的代替 parameterMap 像吻,并且還能自定義 JavaBean 類型的傳參峻黍,所以 parameterMap 退出舞臺(tái),實(shí)屬正常拨匆。

總結(jié)

我一直來都希望自己只輸出觀點(diǎn)姆涩,而不是輸出字典,但其中有些知識(shí)點(diǎn)又是極其冗雜惭每,知識(shí)輸出真是個(gè)難搞的差事骨饿,如何既能把知識(shí)脈絡(luò)梳理的完整亏栈,又能講得淺顯易懂,言簡意賅宏赘,確實(shí)是后續(xù)文章分解輸出的研究方向绒北。

本篇完,本系列下一篇我們講《 Mybatis系列全解(六):Mybatis最硬核的API你知道幾個(gè)察署? 》闷游。

BIU ~ 文章持續(xù)更新,微信搜索「潘潘和他的朋友們」第一時(shí)間閱讀贴汪,隨時(shí)有驚喜脐往。本文會(huì)在 GitHub https://github.com/JavaWorld 收錄,熱騰騰的技術(shù)扳埂、框架业簿、面經(jīng)、解決方案阳懂,我們都會(huì)以最美的姿勢第一時(shí)間送達(dá)梅尤,歡迎 Star ~ 我們未來 不止文章!想進(jìn)讀者群的伙伴歡迎撩我個(gè)人號(hào):panshenlian岩调,備注「加群」我們?nèi)豪餁g聊吧 ~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巷燥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子誊辉,更是在濱河造成了極大的恐慌矾湃,老刑警劉巖亡脑,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堕澄,死亡現(xiàn)場離奇詭異,居然都是意外死亡霉咨,警方通過查閱死者的電腦和手機(jī)蛙紫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來途戒,“玉大人坑傅,你說我怎么就攤上這事∨缯” “怎么了唁毒?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長星爪。 經(jīng)常有香客問我浆西,道長,這世上最難降的妖魔是什么顽腾? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任近零,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘久信。我一直安慰自己窖杀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布裙士。 她就那樣靜靜地躺著入客,像睡著了一般。 火紅的嫁衣襯著肌膚如雪腿椎。 梳的紋絲不亂的頭發(fā)上痊项,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音酥诽,去河邊找鬼鞍泉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛肮帐,可吹牛的內(nèi)容都是我干的咖驮。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼训枢,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼托修!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恒界,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤睦刃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后十酣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涩拙,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年耸采,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兴泥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虾宇,死狀恐怖搓彻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘱朽,我是刑警寧澤旭贬,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站搪泳,受9級(jí)特大地震影響稀轨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜森书,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一靶端、第九天 我趴在偏房一處隱蔽的房頂上張望谎势。 院中可真熱鬧,春花似錦杨名、人聲如沸脏榆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽须喂。三九已至,卻和暖如春趁蕊,著一層夾襖步出監(jiān)牢的瞬間坞生,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工掷伙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留是己,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓任柜,卻偏偏與公主長得像卒废,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子宙地,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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