一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - MyBatis 延遲加載、一級緩存、二級緩存(詳解)

MyBatis 延遲加載、一級緩存、二級緩存(詳解)

2020-12-18 14:26sword-successful Java教程

下面小編就為大家帶來一篇MyBatis 延遲加載、一級緩存、二級緩存(詳解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

使用orm框架我們更多的是使用其查詢功能,那么查詢海量數據則又離不開性能,那么這篇中我們就看下mybatis高級應用之延遲加載一級緩存二級緩存。使用時需要注意延遲加載必須使用resultmap,resulttype不具有延遲加載功能。

一、延遲加載

延遲加載已經是老生常談的問題,什么最大化利用數據庫性能之類之類的,也懶的列舉了,總是我一提到延遲加載腦子里就會想起來了hibernate get和load的區別。ok,廢話少說,直接看代碼。 先來修改配置項xml。

注意,編寫mybatis.xml時需要注意配置節點的先后順序,settings在最前面,否則會報錯。

?
1
2
3
4
<settings>
  <setting name="lazyloadingenabled" value="true"/>
  <setting name="aggressivelazyloading" value="false"/>
 </settings>

MyBatis 延遲加載、一級緩存、二級緩存(詳解)

前面提到延遲加載只能通過association、collection來實現,因為只有存在關聯關系映射的業務場景里你才需要延遲加載,也叫懶加載,也就是常說的用的時候再去加載。ok,那么我們來配一個association來實現:

我來編寫一個加載博客列表的同時加載出博客額作者, 主要功能點在id為blogauthorresumtmap這個resultmap上,其中使用了association,關鍵點是它的select屬性,該屬性也就是你需要懶加載調用的statment id。 當然需要懶加載的statement 返回值當然是resultmap

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<resultmap id="blogauthorresumtmap" type="blog">
  <id column="id" property="id"/>
  <result column="title" property="title"/>
  <result column="category" property="category"/>
  <result column="author_id" property="author_id"/>
  <!--使用assocition支持延遲加載功能,配置延遲加載關聯關系-->
  <association property="author" javatype="author" select="selectauthorbyid" column="author_id"/>
 </resultmap>
 
 <!--要使用延遲記載的方法-->
 <select id="selectblogauthor" resultmap="blogauthorresumtmap">
  select id,title,category,author_id from t_blog
 </select>
 
 <!--延遲加載查詢博客對應的作者方法-->
 <select id="selectauthorbyid" parametertype="int" resulttype="author">
  select id,name from t_author where id=#{value}
 </select>

ok,來看測試結果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@test
 public void getblogauthorbylazyloading(){
  sqlsession sqlsession=null;
  try{
   sqlsession=sqlsessionfactory.opensession();
   list<blog> list = sqlsession.selectlist("com.autohome.mapper.author.selectblogauthor");
 
   for (blog blog:list) {
    system.out.println("id:"+blog.getid()+",title:"+blog.gettitle()+",category:"+blog.getcategory());
    system.out.println("author:"+blog.getauthor().getname());
   }
 
  }catch(exception e){
   e.printstacktrace();
  }finally {
   sqlsession.close();
  }
 }

  MyBatis 延遲加載、一級緩存、二級緩存(詳解)

MyBatis 延遲加載、一級緩存、二級緩存(詳解)

從圖一中看出,執行selectblogauthor返回list<blog>對象時只執行了sql select id,title,category,author_id from t_blog,循環遍歷時才去執行select id,name from t_author where id=?。

二、一級緩存

了解緩存前我們先看一張圖片(圖片來源于傳智播客視頻圖片)。從圖中可以了解一級緩存是sqlsession級別、二級緩存是mapper級別。在操作數據庫時我們需要先構造sqlsession【默認實現是defaultsqlsession.java】,在對象中有一個數據結構【hashmap】來存儲緩存數據。不同的sqlsession區域是互不影響的。 如果同一個sqlsession之間,如果多次查詢之間執行了commit,則緩存失效,mybatis避免臟讀。

MyBatis 延遲加載、一級緩存、二級緩存(詳解)

ok,在看mybatis一級緩存時,我總是覺的一級緩存有點雞肋,兩個查詢如果得到一樣的數據,你還會執行第二次么,果斷引用第一次的返回值了。 可能還沒了解到一級緩存的奧妙之處。一級緩存默認是開啟的,不需要額外設置,直接使用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void testcache(){
  sqlsession sqlsession=null;
  try{
   sqlsession=sqlsessionfactory.opensession();
 
   author author = sqlsession.selectone("com.autohome.mapper.author.selectauthorbyid",1);
   system.out.println("作者信息 id:"+author.getid()+",name:"+author.getname());
 
   author = sqlsession.selectone("com.autohome.mapper.author.selectauthorbyid",1);
   system.out.println("作者信息2 id:"+author.getid()+",name:"+author.getname());
 
  }catch(exception e){
   e.printstacktrace();
  }finally {
   sqlsession.close();
  }
 }

MyBatis 延遲加載、一級緩存、二級緩存(詳解)

從debug截圖來看,當我們第一次調用方法時執行了select id,name from t_author where id=? 此時緩存中還沒有該數據,則執行數據庫查詢,當再次執行時直接從緩存中讀取。

執行demo后我們來看下這個查詢過程保存到緩存的源碼,先看下defaultsqlsession.java。我們調用的selectone(),從代碼中看它是直接調用selectlist()然后判斷返回值size大小。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@override
 public <t> t selectone(string statement) {
 return this.<t>selectone(statement, null);
 }
 
 @override
 public <t> t selectone(string statement, object parameter) {
 // popular vote was to return null on 0 results and throw exception on too many.
 list<t> list = this.<t>selectlist(statement, parameter);
 if (list.size() == 1) {
  return list.get(0);
 } else if (list.size() > 1) {
  throw new toomanyresultsexception("expected one result (or null) to be returned by selectone(), but found: " + list.size());
 } else {
  return null;
 }
 }

再跟蹤到selectlist方法,看到先構造mappedstatement對象,然后看到真正執行query()的是一個executor對象,在defaultsqlsession.java中executor是成員變量,再翻到org.apache.ibatis.executor包中看到executor實際是一個接口。ok,那么我們debug時發現其引用是cachingexecutor。再打開cachingexecutor.java

?
1
2
3
4
5
6
7
8
9
10
11
@override
 public <e> list<e> selectlist(string statement, object parameter, rowbounds rowbounds) {
 try {
  mappedstatement ms = configuration.getmappedstatement(statement);
  return executor.query(ms, wrapcollection(parameter), rowbounds, executor.no_result_handler);
 } catch (exception e) {
  throw exceptionfactory.wrapexception("error querying database. cause: " + e, e);
 } finally {
  errorcontext.instance().reset();
 }
 }

從cachingexecutor.java的兩個query()可以看到先去構造cachekey 再調用抽象類baseexecutor.query(),這個也是最關鍵的一步。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//先創建cachekey
public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler) throws sqlexception {
 boundsql boundsql = ms.getboundsql(parameterobject);
 cachekey key = createcachekey(ms, parameterobject, rowbounds, boundsql);
 return query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
 }
 
//再執行查詢方法
public <e> list<e> query(mappedstatement ms, object parameterobject, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql)
  throws sqlexception {
 cache cache = ms.getcache();
 if (cache != null) {
  flushcacheifrequired(ms);
  if (ms.isusecache() && resulthandler == null) {
  ensurenooutparams(ms, parameterobject, boundsql);
  @suppresswarnings("unchecked")
  list<e> list = (list<e>) tcm.getobject(cache, key);
  if (list == null) {
   list = delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
   tcm.putobject(cache, key, list); // issue #578 and #116
  }
  return list;
  }
 }
 return delegate.<e> query(ms, parameterobject, rowbounds, resulthandler, key, boundsql);
 }

baseexecutor.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public <e> list<e> query(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql) throws sqlexception {
 errorcontext.instance().resource(ms.getresource()).activity("executing a query").object(ms.getid());
 if (closed) {
 throw new executorexception("executor was closed.");
 }
 if (querystack == 0 && ms.isflushcacherequired()) {
 clearlocalcache();
 }
 list<e> list;
 try {
 querystack++;
 list = resulthandler == null ? (list<e>) localcache.getobject(key) : null;
 if (list != null) {
  handlelocallycachedoutputparameters(ms, key, parameter, boundsql);
 } else {
  list = queryfromdatabase(ms, parameter, rowbounds, resulthandler, key, boundsql);
 }
 } finally {
 querystack--;
 }
 if (querystack == 0) {
 for (deferredload deferredload : deferredloads) {
  deferredload.load();
 }
 // issue #601
 deferredloads.clear();
 if (configuration.getlocalcachescope() == localcachescope.statement) {
  // issue #482
  clearlocalcache();
 }
 }
 return list;
}

再看其中關鍵代碼queryfromdatabase

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private <e> list<e> queryfromdatabase(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, cachekey key, boundsql boundsql) throws sqlexception {
 list<e> list;
 localcache.putobject(key, execution_placeholder);
 try {
  list = doquery(ms, parameter, rowbounds, resulthandler, boundsql);
 } finally {
  localcache.removeobject(key);
 }
 localcache.putobject(key, list);
 if (ms.getstatementtype() == statementtype.callable) {
  localoutputparametercache.putobject(key, parameter);
 }
 return list;
 }

ok,看了一長串,終于是有點眉目了,我們看到finally中先刪除當前key緩存,然后再調用localcache.putobject把最新的結果集存入hashmap中。

三、二級緩存

了解二級緩存之前先來看副圖(圖片來自傳智播客視頻,非本人編寫),那么從圖中我們可以看出,mybatis二級緩存是mapper級別,也就是說不同的sqlmapper共享不同的內存區域,不同的sqlsession共享同一個內存區域,用mapper的namespace區別內存區域。

MyBatis 延遲加載、一級緩存、二級緩存(詳解)

開啟mybatis二級緩存: 1、設置mybatis.xml,也就是說mybatis默認二級緩存是關閉的。

2、設置mapper。在mapper.xml內添加標簽:<cache/>

3、pojo實現接口serializable。實現該接口后也就說明二級緩存不僅可以存入內存中,還可以存入磁盤。

ok,看一個二級緩存demo:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@test
 public void testcache2(){
  sqlsession sqlsession=null;
  sqlsession sqlsession2=null;
  try{
   sqlsession=sqlsessionfactory.opensession();
   sqlsession2=sqlsessionfactory.opensession();
 
   author author = sqlsession.selectone("com.autohome.mapper.author.selectauthorbyid",1);
   system.out.println("作者信息 id:"+author.getid()+",name:"+author.getname());
   sqlsession.close();
 
   author author2 = sqlsession2.selectone("com.autohome.mapper.author.selectauthorbyid",1);
   system.out.println("作者信息2 id:"+author2.getid()+",name:"+author2.getname());
   sqlsession2.close();
  }catch(exception e){
   e.printstacktrace();
  }finally {
 
  }
 }

運行demo可以看出二級緩存不同的地方在于cache hit ratio,發出sql查詢時先看是否命中緩存,第一次則是0.0 ,再次查詢時則直接讀取緩存數據,命中率是0.5。當然數據結構還是hashmap。

如果數據實時性要求比較高,可以設置select 語句的 

 MyBatis 延遲加載、一級緩存、二級緩存(詳解)

如果數據的查詢實時性要求比較高,則設置select語句的usecache="false",則每次都直接執行sql。

?
1
2
3
<select id="selectblogauthor" resultmap="blogauthorresumtmap" usecache="false">
  select id,title,category,author_id from t_blog
 </select>

以上這篇mybatis 延遲加載、一級緩存、二級緩存(詳解)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:http://www.cnblogs.com/sword-successful/p/7400685.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本大片在线 | 欧美色综合高清免费 | 欧美成人tv在线观看免费 | 日本mv精品中文字幕 | 调教校花浣肠开菊 | 国色天香视频完整版 | 99在线观看国产 | 日韩精品免费一区二区 | 91色香sxmv最网页版新地址 | 欧美日韩精彩视频 | 我强进了老师身体在线观看 | 91碰碰| 亚洲国产精品嫩草影院久久 | 成人观看免费观看视频 | 日本xxxxx69hd日本| 波多野结衣女教师在线观看 | 蜜月aⅴ免费一区二区三区 蜜桃影像传媒推广 | 亚洲入口| youyjzzcom最新欧美 | 青苹果乐园影院在线播放 | 丝瓜秋葵番茄绿巨人在线观看 | 操大爷影院 | 爆操萝莉 | 亚洲国产成人久久77 | 国内精品91久久久久 | 无码天堂亚洲国产AV久久 | 精品亚洲欧美中文字幕在线看 | 国产自精品| 美女被草哭 | 精品精品精品 | 国产欧美视频高清va在线观看 | 精品欧美一区二区三区四区 | 国产福利兔女郎在线观看 | 91制片在线观看 | 国产成人精品曰本亚洲78 | 国内自拍网红在综合图区 | 爽新片xxxxxxx | 青青草国产精品久久碰 | 教室里的激情电影 | 国产目拍亚洲精品一区二区三区 | 欧美亚洲综合另类 |