mybatis學(xué)習(xí)筆記(關(guān)于映射文件和mybatis緩存)

2018-10-02

<mapper namespace="com.dao.AdministratorDao">
<insert id="insertInfo" useGeneratedKeys="true" keyProperty="id">
insert into administrator(id,name,age,job) VALUES
(#{id},#{administrator.name},${administrator.age},#{administrator.job})
</insert>
</mapper>

只有insert和update標(biāo)簽中有useGenaratedKeys和keyProperty兩個(gè)屬性鳞芙,這兩個(gè)屬性是為了獲取
數(shù)據(jù)庫中自增字段用的,將返回的值復(fù)制給keyProperty中設(shè)置的變量怎爵,然后可以在下面的sql語句這樣
使用悯森,你發(fā)現(xiàn),我的administrator只有插入了三個(gè)屬性巷怜,id屬性是沒有插入的葛超,這個(gè)屬性是為了接收
用的,也就是說在執(zhí)行完這條語句后延塑,我調(diào)用administrator.getId()會(huì)獲取到數(shù)據(jù)庫自增字段id的值绣张。

參考鏈接:https://blog.csdn.net/hellostory/article/details/6790248

對(duì)于不支持自動(dòng)生成主鍵的JDBC驅(qū)動(dòng)或者數(shù)據(jù)庫,需要在insert標(biāo)簽中加上標(biāo)簽<selectKey />
例如:
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
其中屬性值order需要注意关带,這個(gè)屬性值只有兩個(gè)值侥涵,一個(gè)是BEFORE,另一個(gè)是AFTER
如果設(shè)置為 BEFORE宋雏,那么它會(huì)首先選擇主鍵独令,設(shè)置 keyProperty 然后執(zhí)行插入語句。
如果設(shè)置為 AFTER好芭,那么先執(zhí)行插入語句燃箭,然后是 selectKey 元素

<sql>標(biāo)簽可以被用來定義可重用的SQL代碼段,可以包含在其他語句中

<include></include>標(biāo)簽里有個(gè)標(biāo)簽<property>
使用這個(gè)標(biāo)簽的時(shí)候要注意
我們知道在include標(biāo)簽中有個(gè)屬性refid用來指向一個(gè)sql標(biāo)簽的
那么這個(gè)property標(biāo)簽則是對(duì)sql標(biāo)簽中的值進(jìn)行復(fù)制用的舍败,我們來看下例子:
<select id="selectAll" resultType="Administrator">
select * from administrator
where job = <include refid="job"><property name="tt" value="程序員" /></include>

</select>

<sql id="job">
    ${tt}
</sql>

這條語句返回結(jié)果是錯(cuò)的招狸,它顯示找不到程序員這行,
我們?cè)谝谜Z句左右兩邊都加上引號(hào)邻薯,現(xiàn)在我們來看下輸出的sql語句:
select * from administrator where job = " 程序員 "
發(fā)現(xiàn)了吧裙戏,我們傳入值的左右兩邊都有空格,也就是說厕诡,在使用字段是字符串的情況下累榜,不能這樣寫,否則會(huì)出現(xiàn)上面的情況灵嫌。

#{變量名壹罚,屬性:xxxx,屬性:xxx}

關(guān)于使用和不使用@Param()注釋的區(qū)別
1、不使用@Param注釋寿羞,參數(shù)只能有一個(gè)猖凛,并且參數(shù)是對(duì)象,這樣使用#{}和使用{}都可以取值 2绪穆、不使用@Param注釋辨泳,參數(shù)只有一個(gè)虱岂,如果參數(shù)是基本類型和String類型,則使用#{}正常取值菠红,{}取不到值
3第岖、使用@Param注釋,#{}和${}都可以取到值试溯,并且單個(gè)參數(shù)和多個(gè)參數(shù)的情況都適用


2018-10-09

以作用域+返回值+絕對(duì)路徑的方法名
e.g:
public abstract java.until.List com.dao.PersonDao.select_PersonAndJob() 這個(gè)作為key
將MapperMethod作為value

MapperMethod中有兩個(gè)比較重要的內(nèi)部類
1蔑滓、sqlCommand //封裝了SQL標(biāo)簽的類型:insert update delete select
2、MethodSignature //封裝了方法的參數(shù)信息 返回類型信息等

mybatis緩存

1耍共、一級(jí)緩存是通過SqlSession來實(shí)現(xiàn)的

image.png

圖片來自鏈接:https://blog.csdn.net/qq_25689397/article/details/52066179
這是只有mybatis框架的情況下烫饼,緩存的大體過程

如果只有mybatis的時(shí)候猎塞,當(dāng)進(jìn)入到MapperMethod類中調(diào)用selectList方法的時(shí)候试读,進(jìn)入的是DefaultSqlSession類
然后在這個(gè)類中調(diào)用執(zhí)行器executor的query方法,在執(zhí)行器中有個(gè)成員變量用于存儲(chǔ)緩存荠耽,就是PerpetualCache類
這個(gè)類中的成員變量cache存儲(chǔ)緩存钩骇,這個(gè)成員變量類型是hashmap類型,也就是mybatis的緩存數(shù)據(jù)是存儲(chǔ)在hashmap中的

注意:獲取mapperMethod
將路徑作為key铝量,通過方法cachedMapperMethod()方法來操作倘屹,如果key存在則直接取出MapperMethod對(duì)象
如果key不存在則新建一個(gè)mapperMethod對(duì)象,并將對(duì)象存入ConcurrentHashMap集合中慢叨,然后返回該對(duì)象纽匙。
接著是調(diào)用mapperMethod的execute()方法,根據(jù)mapperMethod的command的屬性類型拍谐,進(jìn)行相對(duì)應(yīng)的操作烛缔,
基本就是sqlSession的四個(gè)操作:insert update delete select
下面以select為例
調(diào)試進(jìn)入這個(gè)方法發(fā)現(xiàn)是進(jìn)入SqlSessionTemplate類,因?yàn)槲沂钦虾玫腟SM框架轩拨,所以在這個(gè)時(shí)候是spring在
對(duì)這些sqlSession進(jìn)行代理操作践瓷,在調(diào)用這個(gè)方法的時(shí)候,會(huì)進(jìn)入到這個(gè)類的內(nèi)部類SqlSessionInterceptor
在這個(gè)類invoke方法中亡蓉,我們發(fā)現(xiàn)晕翠,他在finally{}代碼塊中對(duì)sqlSession進(jìn)行了關(guān)閉操作,這也是為什么我們
每一次調(diào)用sql語句砍濒,不是從緩存中取出來淋肾,而是直接跟數(shù)據(jù)庫打交道,也就是每個(gè)session運(yùn)行完一個(gè)方法后爸邢,就
會(huì)被銷毀巫员,即創(chuàng)建快銷毀的也快

2、二級(jí)緩存是通過mapper的namespace實(shí)現(xiàn)共享

也就是不同的session對(duì)于namespace的操作是共享的甲棍,每個(gè)namespace都有一塊緩存简识,在執(zhí)行查詢的時(shí)候默認(rèn)不刷新緩存赶掖,
而在執(zhí)行增刪改的時(shí)候是默認(rèn)要刷新緩存的,mybatis提供給我們兩個(gè)屬性進(jìn)行設(shè)置七扰,一個(gè)是useCache一個(gè)是flushCache
并且默認(rèn)值如下:
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
增刪改是沒有useCache屬性的奢赂,被定死了
mybatis本身的二級(jí)緩存很容易出現(xiàn)問題,也就是到多表操作的時(shí)候颈走,容易出現(xiàn)臟數(shù)據(jù)的問題膳灶,而且mybatis本身的二級(jí)緩存
無法實(shí)現(xiàn)分布式具體的問題,可以查看:https://www.cnblogs.com/liouwei4083/p/6025929.html
基于此立由,mybatis給我們提供了可以自定義的二級(jí)緩存轧钓,也就是添加了Cache接口,實(shí)現(xiàn)這個(gè)接口锐膜,可以自己來實(shí)現(xiàn)緩存毕箍,
通常可以借助實(shí)現(xiàn)這個(gè)接口來使用redis做mybatis的二級(jí)緩存道盏,然后再在配置文件里的<cache />標(biāo)簽里添加這個(gè)類的路徑而柑,
這樣便可以實(shí)現(xiàn)分布式。

SSM框架整合后荷逞,我們看下調(diào)試過程
我們看下代碼的實(shí)現(xiàn)媒咳,首先進(jìn)入MapperProxy類,也就是spring代理類

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }   

這個(gè)方法中种远,最重要的是最后兩行的代碼涩澡,cachedMapperMethod()方法是判斷當(dāng)前方法路徑下是否有緩存,有緩存則將緩存返回坠敷,沒有緩存妙同,則重新創(chuàng)建一個(gè)MapperMethod對(duì)象,并且將對(duì)象存入ConcurrentHashMap中常拓,這也是跟單獨(dú)使用mybatis的區(qū)別之一渐溶,存儲(chǔ)容器不同。然后通過MapperMethod的方法execute()執(zhí)行sql語句弄抬。

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //根據(jù)sql的類型茎辐,選擇不同的執(zhí)行方式
    switch (command.getType()) {
      case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
      //當(dāng)前方法調(diào)試的入口
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName() 
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }   

我們可以看到不管是增刪改查哪一個(gè)都會(huì)調(diào)用sqlSession對(duì)應(yīng)的方法,程序進(jìn)一步進(jìn)行掂恕,會(huì)到類SqlSessionTemplate類拖陆,調(diào)用selectList()方法,在調(diào)用這個(gè)方法的時(shí)候,會(huì)先進(jìn)入到這個(gè)類的內(nèi)部類SqlSessionInterceptor中懊亡,也就是Session的攔截器依啰,調(diào)用了攔截器的Invoke方法,我們看下這個(gè)方法的實(shí)現(xiàn):

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          //注意每次SqlSession操作數(shù)據(jù)庫后店枣,都會(huì)自動(dòng)斷開速警,這也是為什么mybatis一級(jí)緩存會(huì)失效的原因
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }   

看到這塊基本就能了解叹誉,為什么mybatis的一級(jí)緩存在SSM框架中會(huì)失效了,也就是spring管理使用sqlSession后闷旧,每次使用都會(huì)關(guān)閉长豁,也就是sqlSession的創(chuàng)建和關(guān)閉變得頻繁了

參考鏈接:
https://www.cnblogs.com/volatileAndCrazy/p/7826331.html
https://blog.csdn.net/z69183787/article/details/78402892
https://www.cnblogs.com/winclpt/articles/7511672.html
https://blog.csdn.net/qq_25689397/article/details/52066179
https://www.cnblogs.com/linkstar/p/7182695.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市忙灼,隨后出現(xiàn)的幾起案子匠襟,更是在濱河造成了極大的恐慌,老刑警劉巖该园,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酸舍,死亡現(xiàn)場離奇詭異,居然都是意外死亡里初,警方通過查閱死者的電腦和手機(jī)啃勉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來青瀑,“玉大人璧亮,你說我怎么就攤上這事萧诫〕饽眩” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵帘饶,是天一觀的道長哑诊。 經(jīng)常有香客問我,道長及刻,這世上最難降的妖魔是什么镀裤? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮缴饭,結(jié)果婚禮上暑劝,老公的妹妹穿的比我還像新娘。我一直安慰自己颗搂,他們只是感情好担猛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丢氢,像睡著了一般傅联。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疚察,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天蒸走,我揣著相機(jī)與錄音,去河邊找鬼貌嫡。 笑死比驻,一個(gè)胖子當(dāng)著我的面吹牛该溯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播别惦,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼朗伶,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了步咪?” 一聲冷哼從身側(cè)響起论皆,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猾漫,沒想到半個(gè)月后点晴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡悯周,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年粒督,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禽翼。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屠橄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出闰挡,到底是詐尸還是另有隱情锐墙,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布长酗,位于F島的核電站溪北,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏夺脾。R本人自食惡果不足惜之拨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望咧叭。 院中可真熱鬧蚀乔,春花似錦、人聲如沸菲茬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽生均。三九已至听想,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間马胧,已是汗流浹背汉买。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留佩脊,地道東北人蛙粘。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓垫卤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親出牧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子穴肘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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