溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

深入淺出MyBatis:「映射器」全了解

發(fā)布時(shí)間:2020-07-26 19:15:59 來(lái)源:網(wǎng)絡(luò) 閱讀:954 作者:情情說 欄目:開發(fā)技術(shù)

本篇文章是「深入淺出MyBatis:技術(shù)原理與實(shí)踐」書籍的總結(jié)筆記。

上一篇總結(jié)了MyBatis的配置,詳細(xì)說明了各個(gè)配置項(xiàng),其中提到了映射器,它是MyBatis最強(qiáng)大的工具,也是使用最多的工具。

通過映射器,可以很容易的進(jìn)行數(shù)據(jù)的增刪改查操作,我們抽象下進(jìn)行這些操作的關(guān)鍵點(diǎn):傳遞查詢參數(shù)、組裝各種場(chǎng)景下的查詢條件、關(guān)聯(lián)查詢、將查詢結(jié)果映射為Java Bean對(duì)象或集合等。另外,可以通過延遲加載、緩存提高數(shù)據(jù)查詢的性能。

本篇就按照這個(gè)思路進(jìn)行總結(jié),首先列舉下映射器的主要元素,每個(gè)元素提供的配置項(xiàng)和作用,然后重點(diǎn)介紹參數(shù)、結(jié)果映射、延遲加載、緩存、動(dòng)態(tài)SQL等功能。

映射器的主要元素

映射器是由Java接口和XML文件(或注解)共同組成的,Java接口主要定義調(diào)用者接口,XML文件是配置映射器的核心文件,包括以下元素:

  • select 查詢語(yǔ)句,可以自定義參數(shù),返回結(jié)果集;
  • insert 插入語(yǔ)句,返回一個(gè)整數(shù),表示插入的條數(shù);
  • update 更新語(yǔ)句,返回一個(gè)整數(shù),表示更新的條數(shù);
  • delete 刪除語(yǔ)句,返回一個(gè)整數(shù),表示刪除的條數(shù);
  • sql 允許定義一部分SQL,然后再各個(gè)地方引用;
  • resultMap 用來(lái)描述從數(shù)據(jù)庫(kù)結(jié)果集中來(lái)加載對(duì)象,還可以配置關(guān)聯(lián)關(guān)系;
  • cache 給定命名空間的緩存配置;
增、刪、改、查操作
查找

執(zhí)行select語(yǔ)句前,需要定義參數(shù),執(zhí)行后,也提供了強(qiáng)大的映射規(guī)則或自動(dòng)映射,將返回的結(jié)果集綁定到j(luò)ava bean中。

select元素有很多配置項(xiàng),下面簡(jiǎn)單說明下:

  • paramterType:傳入的參數(shù)類型,可以是基本類型、map、自定義的java bean;
  • resultType:返回的結(jié)果類型,可以是基本類型、自定義的java bean;
  • resultMap:它是最復(fù)雜的元素,可以配置映射規(guī)則、級(jí)聯(lián)、typeHandler等,與ResultType不能同時(shí)存在;
  • flushCache:在調(diào)用SQL后,是否要求清空之前查詢的本地緩存和二級(jí)緩存,主要用于更新緩存,默認(rèn)為false;
  • useCache:?jiǎn)?dòng)二級(jí)緩存的開關(guān),默認(rèn)只會(huì)啟動(dòng)一級(jí)緩存;
  • timeout:設(shè)置超時(shí)參數(shù),等超時(shí)的時(shí)候?qū)伋霎惓?,單位為秒?/li>
  • fetchSize:獲取記錄的總條數(shù)設(shè)定;

比如根據(jù)米聊號(hào)獲取用戶信息:

<select id="findByMiliao" parameterType="string" resultType="User">
        select
        u.*
        from mxt_user u
        where u.miliao=#{miliao}
</select>

上一篇介紹配置時(shí),有個(gè)設(shè)置項(xiàng)autoMappingBehavior,默認(rèn)為自動(dòng)映射沒有定義嵌套結(jié)果集映射的結(jié)果集;還有設(shè)置項(xiàng)mapUnderscoreToCamelCase,設(shè)置為true時(shí),會(huì)自動(dòng)將以「下劃線」命名的數(shù)據(jù)庫(kù)字段名,自動(dòng)映射為以「駝峰式」命名的POJO。

傳遞多個(gè)參數(shù)時(shí),有3種方式:

  • 使用Map參數(shù);
  • 使用注解方式傳遞;
  • 使用java bean;

使用注解方式如下:

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

使用Map傳遞參數(shù),會(huì)導(dǎo)致業(yè)務(wù)可讀性喪失,導(dǎo)致以后擴(kuò)展和維護(hù)不方便,不建議;如果參數(shù)個(gè)數(shù)<=5,建議使用注解的方式,因?yàn)檫^多參數(shù)將給調(diào)用者帶來(lái)困難;如果參數(shù)個(gè)數(shù)>5,建議使用JavaBean方式;

使用resultMap映射結(jié)果集,后面會(huì)單獨(dú)介紹。

insert

屬性和select大部分都相同, 說下3個(gè)不同的屬性:

  • keyProperty:指定哪個(gè)列是主鍵,如果是聯(lián)合主鍵可以用逗號(hào)隔開;
  • keyColumn:指定第幾列是主鍵,不能和keyProperty共用;
  • useGeneratedKeys:是否使用自動(dòng)增長(zhǎng),默認(rèn)為false;

當(dāng)useGeneratedKeys設(shè)為true時(shí),在插入的時(shí)候,會(huì)回填Java Bean的id值,通過返回的對(duì)象可獲取主鍵值。

如果想根據(jù)一些特殊關(guān)系設(shè)置主鍵的值,可以在insert標(biāo)簽內(nèi)使用selectKey標(biāo)簽,比如:如果t_role沒有記錄,則需要設(shè)置為1,否則取最大id加2:

<insert id="insertRole" useGeneratedKeys="true" keyProperty="id" >
    <selectKey keyProperty="id" resultType="int" order="before">
        select if(max(id) is null,1,max(id)+2) as newId from t_role
    </selectKey> 
</insert>
update和delete

比較簡(jiǎn)單,就不過多介紹了。

參數(shù)

上面已經(jīng)介紹了參數(shù)傳遞,另外可以指定參數(shù)的類型去讓對(duì)應(yīng)的typeHandler處理它們。

#{age , javaType=int , jdbcType=NUMERIC }

還可以對(duì)一些數(shù)值型的參數(shù)設(shè)置其保存的精度

#{price, javaType=double , jdbcType=NUMERIC , numericScale=2 }

一般都是傳遞字符串,設(shè)置的參數(shù)#{name}大部分情況下,會(huì)創(chuàng)建預(yù)編譯語(yǔ)句,但有時(shí)候傳遞的是SQL語(yǔ)句本身,不是需要的參數(shù),可以通過$符號(hào)表示,比如傳遞參數(shù)columns為"col1,col2,col3",可以寫成下面語(yǔ)句:

select ${columns} from t_tablename

但要注意sql的安全性,防止sql注入。

sql元素

定義:

<sql id="role_columns">
    id,role_name,note
</sql>

使用:

<include refid="role_columns">
    <property name="prefix" value="r" />
</include>
結(jié)果映射
元素介紹

resultMap是MyBatis里面最復(fù)雜的元素,它的作用是定義映射規(guī)則、級(jí)聯(lián)的更新、定制類型轉(zhuǎn)換器等。

由以下元素構(gòu)成:

<resultMap>
    <constructor> <!-- 配置構(gòu)造方法 -->
        <idArg/>
        <arg/>
    </constructor>
    <id/> <!--指明哪一列是主鍵-->
    <result/> <!--配置映射規(guī)則-->
    <association/> <!--一對(duì)一-->
    <collection/> <!--一對(duì)多-->
    <discriminator> <!--鑒別器級(jí)聯(lián)-->
        <case/>
    </discriminator>
</resultMap>

有的實(shí)體不存在沒有參數(shù)的構(gòu)造方法,需要使用constructor配置有參數(shù)的構(gòu)造方法:

<resultMap id="role" type="com.xiaomi.kfs.mcc.core.domain.Role">
    <constructor>
        <idArg column="id" javaType="int"/>
        <arg column="role_name" javaType="string"/>
    </constructor>
</resultMap>

id指明主鍵列,result配置數(shù)據(jù)庫(kù)字段和POJO屬性的映射規(guī)則:

<resultMap id="role" type="com.xiaomi.kfs.mcc.core.domain.Role">
    <id property="id" column="id" />
    <result property="roleName" column="role_name" />
    <result property="note" column="note" />
</resultMap>

association、collection用于配置級(jí)聯(lián)關(guān)系的,分別為一對(duì)一和一對(duì)多,實(shí)際中,多對(duì)多關(guān)系的應(yīng)用不多,因?yàn)楸容^復(fù)雜,會(huì)用一對(duì)多的關(guān)系把它分解為雙向關(guān)系。

discriminator用于這樣一種場(chǎng)景:比如我們?nèi)ンw檢,男和女的體檢項(xiàng)目不同,如果讓男生去檢查婦科項(xiàng)目,是不合理的, 通過discriminator可以根據(jù)性別,返回不同的對(duì)象。

級(jí)聯(lián)關(guān)系的配置比較多,就不在此演示了,可查看文檔進(jìn)行了解。

延遲加載

級(jí)聯(lián)的優(yōu)勢(shì)是能夠方便地獲取數(shù)據(jù),但有時(shí)不需要獲取所有數(shù)據(jù),這樣會(huì)多執(zhí)行幾條SQL,性能下降,為了解決這個(gè)問題,需要使用延遲加載,只要使用相關(guān)級(jí)聯(lián)數(shù)據(jù)時(shí),才會(huì)發(fā)送SQL去取回?cái)?shù)據(jù)。

在MyBatis的配置中有2個(gè)全局的參數(shù) lazyLoadingEnabled 和 aggressiveLazyLoading ,第一個(gè)的含義是是否開啟延遲加載功能,第二個(gè)的含義是對(duì)任意延遲加載屬性的調(diào)用,會(huì)使延遲加載的對(duì)象完整加載,否則只會(huì)按需加載。

再理解下aggressiveLazyLoading屬性,比如學(xué)生對(duì)象的關(guān)聯(lián)對(duì)象如下:

深入淺出MyBatis:「映射器」全了解

當(dāng)訪問學(xué)生信息的時(shí)候,會(huì)根據(jù)鑒別器把健康的情況也會(huì)查找出來(lái);當(dāng)訪問課程成績(jī)的時(shí)候,同時(shí)也會(huì)把學(xué)生證信息查找出來(lái),因?yàn)樵谀J(rèn)情況下,MyBatis是按層級(jí)延遲加載的。 但這不是我們需要的,并不希望在訪問成績(jī)的時(shí)候,去加載學(xué)生證的信息,可以設(shè)置aggressiveLazyLoading為false,按需進(jìn)行延遲加載數(shù)據(jù)。

上面的2個(gè)屬性都是全局設(shè)置,也可以在association和collection元素上加上屬性值fetchType,它有兩個(gè)取值eager和lazy。

緩存

在沒有顯示配置緩存時(shí),只開啟一級(jí)緩存,一級(jí)緩存是相對(duì)于同一個(gè)SqlSession而言的,在參數(shù)和SQL完全一樣的情況下,使用同一個(gè)SqlSession對(duì)象調(diào)用同一個(gè)Mapper的方法,只會(huì)執(zhí)行一次SQL。

如果是不同的SqlSession對(duì)象,因?yàn)椴煌琒qlSession是相互隔離的,即使用相同的Mapper、參數(shù)和方法,還是會(huì)再次發(fā)送SQL到數(shù)據(jù)庫(kù)去執(zhí)行。

二級(jí)緩存是SqlSessionFactory層面上的,需要進(jìn)行顯示配置,實(shí)現(xiàn)二級(jí)緩存的時(shí)候,要求POJO必須是可序列化的,只需要簡(jiǎn)單配置即可:

<cache />

這樣很多設(shè)置是默認(rèn)的,有如下屬性可以配置:

  • eviction:代表緩存回收策略,可選值有LRU最少使用、FIFO先進(jìn)先出、SOFT軟引用,WEAK弱引用;
  • flushInterval:刷新間隔時(shí)間,單位為毫秒,如果不配置,當(dāng)SQL被執(zhí)行時(shí)才會(huì)刷新緩存;
  • size:引用數(shù)目,代表緩存最多可以存儲(chǔ)多少對(duì)象,不宜設(shè)置過大,設(shè)置過大會(huì)導(dǎo)致內(nèi)存溢出;
  • readOnly:只讀,意味著緩存數(shù)據(jù)只能讀取不能修改;

在大型服務(wù)器上,可能會(huì)使用專用的緩存服務(wù)器,比如Redis緩存,可以通過實(shí)現(xiàn)org.apache.ibatis.cache.Cache接口很方便的實(shí)現(xiàn):

public interface Cache {
    String getId(); //緩存編號(hào)
    void putObject(Object var1, Object var2); //保存對(duì)象
    Object getObject(Object var1); //獲取對(duì)象
    Object removeObject(Object var1); //移除對(duì)象
    void clear(); //清空緩存
    int getSize(); //獲取緩存對(duì)象大小
    ReadWriteLock getReadWriteLock(); //獲取緩存的讀寫鎖
}
動(dòng)態(tài)SQL

很多時(shí)候,需要根據(jù)不同的場(chǎng)景組裝查詢條件,MyBatis提供對(duì)SQL語(yǔ)句動(dòng)態(tài)的組裝能力。

主要提供以下幾種元素:

  • if:判斷語(yǔ)句,但條件分支判斷;
  • choose (when、otherwise):多條件分支判斷;
  • trim (where、set):處理一些SQL拼裝問題;
  • foreach:循環(huán)語(yǔ)句,在in語(yǔ)句等列舉條件常用;
  • bind:通過OGNL表達(dá)式去自定義一個(gè)上下文變量,可以方便使用;

trim可以處理 and 和 逗號(hào) 拼接的問題,舉例如下:

<select id="findRoles" parameterType="string" >
  select id,role_name,note from t_role
  <trim prefix="where" prefixOverrides="and">
      <if test="roleName!=null and roleName!=''">
        and role_name like concat('%',#{roleName},'%')
      </if>
  </trim>
</select>

另外,可以使用set元素設(shè)置更新的字段列表:

<update id="updateRole" parameterType="role">
    update t_role
    <set>
        <if test="roleName!=null and roleName!=''">
            role_name=#{roleName},
        </if>
        <if test="note!=null and note!=''">
            note=#{note}
        </if>
    </set>
    where id=#{id}
</update>

下一篇會(huì)介紹MyBatis的解析和運(yùn)行原理。

歡迎掃描下方二維碼,關(guān)注我的個(gè)人微信公眾號(hào) ~

深入淺出MyBatis:「映射器」全了解

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI