欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2017年08月21日 08:49:38   投稿:jingxian  
下面小編就為大家?guī)硪黄狹yBatis 延遲加載、一級緩存、二級緩存(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

使用ORM框架我們更多的是使用其查詢功能,那么查詢海量數(shù)據(jù)則又離不開性能,那么這篇中我們就看下mybatis高級應(yīng)用之延遲加載、一級緩存、二級緩存。使用時需要注意延遲加載必須使用resultMap,resultType不具有延遲加載功能。

一、延遲加載

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

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

<settings>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="false"/>
 </settings>

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

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

<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支持延遲加載功能,配置延遲加載關(guān)聯(lián)關(guān)系-->
  <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>

 <!--延遲加載查詢博客對應(yīng)的作者方法-->
 <select id="selectAuthorById" parameterType="int" resultType="Author">
  SELECT id,name from t_author where id=#{value}
 </select>

OK,來看測試結(jié)果:

@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();
  }
 }

  

從圖一中看出,執(zhí)行selectBlogAuthor返回List<Blog>對象時只執(zhí)行了SQL SELECT id,title,category,author_id from t_blog,循環(huán)遍歷時才去執(zhí)行select id,name from t_author where id=?。

二、一級緩存

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

OK,在看mybatis一級緩存時,我總是覺的一級緩存有點雞肋,兩個查詢?nèi)绻玫揭粯拥臄?shù)據(jù),你還會執(zhí)行第二次么,果斷引用第一次的返回值了。 可能還沒了解到一級緩存的奧妙之處。一級緩存默認是開啟的,不需要額外設(shè)置,直接使用。

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();
  }
 }

從DEBUG截圖來看,當(dāng)我們第一次調(diào)用方法時執(zhí)行了SELECT id,name from t_author where id=? 此時緩存中還沒有該數(shù)據(jù),則執(zhí)行數(shù)據(jù)庫查詢,當(dāng)再次執(zhí)行時直接從緩存中讀取。

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

@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方法,看到先構(gòu)造MappedStatement對象,然后看到真正執(zhí)行query()的是一個executor對象,在DefaultSqlSession.java中executor是成員變量,再翻到org.apache.ibatis.executor包中看到executor實際是一個接口。OK,那么我們debug時發(fā)現(xiàn)其引用是CachingExecutor。再打開CachingExecutor.java

@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()可以看到先去構(gòu)造CacheKey 再調(diào)用抽象類BaseExecutor.query(),這個也是最關(guān)鍵的一步。

//先創(chuàng)建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);
 }
 
//再執(zhí)行查詢方法
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

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;
}

再看其中關(guān)鍵代碼queryFromDatabase

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中先刪除當(dāng)前key緩存,然后再調(diào)用localCache.putObject把最新的結(jié)果集存入HashMap中。

三、二級緩存

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

開啟mybatis二級緩存: 1、設(shè)置mybatis.xml,也就是說mybatis默認二級緩存是關(guān)閉的。

2、設(shè)置mapper。在mapper.xml內(nèi)添加標簽:<cache/>

3、pojo實現(xiàn)接口Serializable。實現(xiàn)該接口后也就說明二級緩存不僅可以存入內(nèi)存中,還可以存入磁盤。

OK,看一個二級緩存demo:

@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,發(fā)出sql查詢時先看是否命中緩存,第一次則是0.0 ,再次查詢時則直接讀取緩存數(shù)據(jù),命中率是0.5。當(dāng)然數(shù)據(jù)結(jié)構(gòu)還是HashMap。

如果數(shù)據(jù)實時性要求比較高,可以設(shè)置select 語句的 

 

如果數(shù)據(jù)的查詢實時性要求比較高,則設(shè)置select語句的useCache="false",則每次都直接執(zhí)行sql。

<select id="selectBlogAuthor" resultMap="blogAuthorResumtMap" useCache="false">
  SELECT id,title,category,author_id FROM t_blog
 </select>

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

相關(guān)文章

  • Java服務(wù)器端跨域問題解決方案

    Java服務(wù)器端跨域問題解決方案

    這篇文章主要介紹了java服務(wù)器端跨域問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-01-01
  • java分布式基于RestTemplate的使用方法

    java分布式基于RestTemplate的使用方法

    這篇文章主要為大家介紹了java分布式基于RestTemplate的使用方法說明,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-02-02
  • Java線程調(diào)度之線程休眠用法分析

    Java線程調(diào)度之線程休眠用法分析

    這篇文章主要介紹了Java線程調(diào)度之線程休眠用法,較為詳細的分析了Java線程休眠的功能與實現(xiàn)技巧,需要的朋友可以參考下
    2015-06-06
  • springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實現(xiàn)

    springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實現(xiàn)

    本文主要介紹了springboot-jta-atomikos多數(shù)據(jù)源事務(wù)管理實現(xiàn),文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Java用jxl讀取excel并保存到數(shù)據(jù)庫的方法

    Java用jxl讀取excel并保存到數(shù)據(jù)庫的方法

    這篇文章主要為大家詳細介紹了Java用jxl讀取excel并保存到數(shù)據(jù)庫的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-10-10
  • SpringBoot整合OpenCV的實現(xiàn)示例

    SpringBoot整合OpenCV的實現(xiàn)示例

    這篇文章主要介紹了SpringBoot整合OpenCV的實現(xiàn)示例。文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java集合框架ArrayList源碼分析(一)

    Java集合框架ArrayList源碼分析(一)

    這篇文章主要為大家詳細介紹了Java集合框架ArrayList源碼分析,感興趣的小伙伴們可以參考一下
    2016-08-08
  • JSON for java快速入門總結(jié)學(xué)習(xí)

    JSON for java快速入門總結(jié)學(xué)習(xí)

    這篇文章主要介紹了JSON for java入門總結(jié)學(xué)習(xí),有需要的可以了解一下。
    2016-11-11
  • 解讀Java中Set真的是無序的嗎

    解讀Java中Set真的是無序的嗎

    這篇文章主要介紹了Java中Set是不是無序的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • SpringBoot項目中feignClient使用方式

    SpringBoot項目中feignClient使用方式

    文章介紹了在Spring Boot項目中配置Feign客戶端攔截器的具體步驟,包括在application.yml中添加配置、在主類上啟用組件掃描、將攔截器加入到攔截器列表中以及在接口調(diào)用時的說明,總結(jié)指出這是個人經(jīng)驗分享,希望對大家有所幫助
    2024-11-11

最新評論