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

解析Mybatis延遲加載問題

 更新時間:2021年05月07日 10:57:07   作者:勒城  
這篇文章主要介紹了Mybatis的延遲加載問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

延遲加載問題

MyBatis針對關(guān)聯(lián)表中的數(shù)據(jù)支持延遲加載。延遲加載其實就是將數(shù)據(jù)加載時機推遲,比如推遲嵌套查詢的執(zhí)行時機。

延遲加載可以實現(xiàn)先查詢主表,按需實時做關(guān)聯(lián)查詢,返回關(guān)聯(lián)表結(jié)果集,一定程度上提高了效率。

<settings> 
        <!-- 啟用延遲加載特性,不配置默認關(guān)閉該特性-->
        <setting name="lazyLoadingEnabled" value="true" /> 
        <!-- 按需加載: false:使用關(guān)聯(lián)屬性時才進行加載; true加載對象,則加載所有屬性 -->
        <setting name="aggressiveLazyLoading" value="false"/> 
</settings> 

lazyLoadingEnabled:是否啟用延遲加載,默認值為false,不啟用延遲加載。lazyLoadingEnabled屬性控制全局是否使用延遲加載,特殊關(guān)聯(lián)關(guān)系也可以通過嵌套查詢中fetchType屬性單獨配置(fetchType屬性值可以是lazy或者eager)

aggressiveLazyLoading:是否按需加載屬性,默認值false,lazyLoadingEnabled屬性啟用時只要加載對象,就會加載該對象的所有屬性;關(guān)閉該屬性則會按需加載,即使用到某關(guān)聯(lián)屬性時,實時執(zhí)行嵌套查詢加載該屬性

對一

 <resultMap id="ExtResultMap" type="com.yan.entity.User" extends="BaseResultMap">
    <association property="role" select="com.yan.dao.RoleMapper.selectByPrimaryKey" column="role_id"/>
  </resultMap>

如果不訪問role屬性,則不會執(zhí)行t_roles表的查詢。當訪問role屬性時才會執(zhí)行查詢操作,而且如果session關(guān)閉,則自動新打開session執(zhí)行查詢

對多

 <resultMap id="ExtResultMap" type="com.yan.entity.Role" extends="BaseResultMap">
    <collection property="users" ofType="com.yan.entity.User" column="id" select="com.yan.dao.UserMapper.selectByRoleId"/>
  </resultMap>

緩存

MyBatis支持一二級緩存
Mybatis提供查詢緩存,如果緩存中有數(shù)據(jù)就不用從數(shù)據(jù)庫中獲取,用于減輕數(shù)據(jù)壓力,提高系統(tǒng)性能

緩存的重要性是不言而喻的。 使用緩存可以避免頻繁的與數(shù)據(jù)庫進行交互, 尤其是在查詢越多、緩存命中率越高的情況下, 使用緩存對性能的提高更明顯。

mybatis也提供了對緩存的支持, 分為一級緩存和二級緩存。 但是在默認的情況下, 只開啟一級緩存(一級緩存是對同一個 SqlSession 而言的)

  • 默認情況下,只有一級緩存(SqlSession級別的緩存,也稱為本地緩存)開啟
  • 二級緩存需要手動開啟和配置,是基于namespace級別的緩存
  • 為了提高擴展性。MyBatis定義了緩存接口Cache??梢酝ㄟ^實現(xiàn)Cache接口來自定義二級緩存
  • 一級緩存是SqlSession級別的緩存。在操作數(shù)據(jù)庫時需要構(gòu)造sqlSession對象,在對象中有一個數(shù)據(jù)結(jié)構(gòu)HashMap用于存儲緩存數(shù)據(jù)。不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域HashMap是互相不影響的
  • 二級緩存是mapper級別的緩存,多個SqlSession去操作同一個Mapper的sql語句,多個SqlSession可以共用二級緩存,二級緩存是跨SqlSession的

一級緩存

一級緩存即local cache本地緩存,作用域默認為sqlSession。當Session flush或close后該Session中的所有Cache將被清空

RoleMapper rm= MybatisSessionFactory.getMapper(RoleMapper.class);
        Role role=rm.selectByPrimaryKey(1L);
        System.out.println(role.getId()+":"+role.getName());
        System.out.println("--------------------------------");
        MybatisSessionFactory.closeSession();
        rm= MybatisSessionFactory.getMapper(RoleMapper.class);
        role=rm.selectByPrimaryKey(1L);
        System.out.println(role.getId()+":"+role.getName());

兩次查詢操作,分別是2次sql語句,證明緩存是session級的。如果不關(guān)閉session,即使2次獲取Mapper執(zhí)行的查詢?nèi)耘f只有一個sql語句

每個SqlSession中持有了Executor,每個Executor中有一個LocalCache。當用戶發(fā)起查詢時,MyBatis根據(jù)當前執(zhí)行的語句生成MappedStatement,在Local Cache進行查詢,如果緩存命中的話,直接返回結(jié)果給用戶,如果緩存沒有命中的話,查詢數(shù)據(jù)庫,結(jié)果寫入Local Cache,最后返回結(jié)果給用戶

一級緩存配置

開發(fā)者只需在MyBatis的配置文件localCacheScope中可以設(shè)置使用一級緩存。共有兩個選項SESSION或者STATEMENT,默認是SESSION級別,即在一個MyBatis會話中執(zhí)行的所有語句,都會共享這一個緩存。一種是STATEMENT級別,可以理解為緩存只對當前執(zhí)行的這一個Statement有效。

 UserMapper userMapper = MyBatisSessionFactory.getMapper(UserMapper.class);
User user1=userMapper.loadById(1L);
System.out.println(user1);

SqlSession session = MyBatisSessionFactory.openSession();
session.clearCache();//清空緩存 ,后續(xù)查詢會發(fā)送SQL語句
User user2=userMapper.loadById(1L);
System.out.println(user2);
System.out.println(user1==user2);

MyBatisSessionFactory.closeSession();

1、第一次發(fā)起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,如果沒有,從數(shù)據(jù)庫查詢用戶信息。得到用戶信息,將用戶信息存儲到一級緩存中。

2、如果中間sqlSession去執(zhí)行commit操作(執(zhí)行插入、更新、刪除),則會清空SqlSession中的一級緩存,這樣做的目的為了讓緩存中存儲的是最新的信息,避免臟讀。

3、第二次發(fā)起查詢用戶id為1的用戶信息,先去找緩存中是否有id為1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。

##源碼分析

SqlSession對外提供了用戶和數(shù)據(jù)庫之間交互需要的所有方法,隱藏了底層的細節(jié)。默認實現(xiàn)類是DefaultSqlSession

Executor: SqlSession向用戶提供操作數(shù)據(jù)庫的方法,但和數(shù)據(jù)庫操作有關(guān)的職責都會委托給Executor。

BaseExecutor是一個實現(xiàn)了Executor接口的抽象類,定義若干抽象方法,在執(zhí)行的時候,把具體的操作委托給子類進行執(zhí)行。

Cache: MyBatis中的Cache接口,提供了和緩存相關(guān)的最基本的操作

BaseExecutor成員變量之一的PerpetualCache,是對Cache接口最基本的實現(xiàn),其實現(xiàn)非常簡單,內(nèi)部持有HashMap,對一級緩存的操作實則是對HashMap的操作。

1、為執(zhí)行和數(shù)據(jù)庫的交互,首先需要初始化SqlSession,通過DefaultSqlSessionFactory開啟SqlSession

2、在初始化SqlSesion時,會使用Configuration類創(chuàng)建一個全新的Executor,作為DefaultSqlSession構(gòu)造函數(shù)的參數(shù)

3、SqlSession創(chuàng)建完畢后,根據(jù)Statment的不同類型,會進入SqlSession的不同方法中,如果是Select語句的話,最后會執(zhí)行到SqlSession的selectList

4、SqlSession把具體的查詢職責委托給了Executor。如果只開啟了一級緩存的話,首先會進入BaseExecutor的query方法。

5、會先根據(jù)傳入的參數(shù)生成CacheKey,默認將MappedStatement的Id、sql的offset、Sql的limit、Sql本身以及Sql中的參數(shù)傳入了CacheKey這個類,最終構(gòu)成CacheKey

6、如果查不到的話,就從數(shù)據(jù)庫查,在queryFromDatabase中,會對localcache進行寫入。 在query方法執(zhí)行的最后,會判斷一級緩存級別是否是STATEMENT級別,如果是的話,就清空緩存,這也就是STATEMENT級別的一級緩存無法共享localCache的原因。

##總結(jié)

MyBatis一級緩存的生命周期和SqlSession一致。MyBatis一級緩存內(nèi)部設(shè)計簡單,只是一個沒有容量限定的HashMap,在緩存的功能性上有所欠缺。MyBatis的一級緩存最大范圍是SqlSession內(nèi)部,有多個SqlSession或者分布式的環(huán)境下,數(shù)據(jù)庫寫操作會引起臟數(shù)據(jù),建議設(shè)定緩存級別為Statement。

##一級緩存的生命周期

MyBatis在開啟一個數(shù)據(jù)庫會話時,會 創(chuàng)建一個新的SqlSession對象,SqlSession對象中會有一個新的Executor對象,Executor對象中持有一個新的PerpetualCache對象;當會話結(jié)束時,SqlSession對象及其內(nèi)部的Executor對象還有PerpetualCache對象也一并釋放掉。如果SqlSession調(diào)用了close()方法,會釋放掉一級緩存PerpetualCache對象,一級緩存將不可用;如果SqlSession調(diào)用了clearCache(),會清空PerpetualCache對象中的數(shù)據(jù),但是該對象仍可使用;SqlSession中執(zhí)行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache對象的數(shù)據(jù),但是該對象可以繼續(xù)使用

##一級緩存的不足

使用一級緩存的時候,因為緩存不能跨會話共享,不同的會話之間對于相同的數(shù)據(jù)可能有不一樣的緩存。在有多個會話或者分布式環(huán)境下,會存在臟數(shù)據(jù)的問題。如果要解決這個問題,就要用到二級緩存。

MyBatis 一級緩存(MyBaits 稱其為 Local Cache)無法關(guān)閉,但是有兩種級別可選:

session級別的緩存,在同一個 sqlSession 內(nèi),對同樣的查詢將不再查詢數(shù)據(jù)庫,直接從緩存中。

statement級別的緩存,session級別緩存不能獲取最新數(shù)據(jù): 為了避免這個問題,可以將一級緩存的級別設(shè)為 statement 級別的,這樣每次查詢結(jié)束都會清掉一級緩存。

由于不同的sqlSession之間的緩存數(shù)據(jù)區(qū)域不共享,如果使用多個SqlSession對數(shù)據(jù)庫進行操作時,就會出現(xiàn)臟數(shù)據(jù)

##二級緩存

一級緩存中,其最大的共享范圍就是一個SqlSession內(nèi)部,如果多個SqlSession之間需要共享緩存,則需要使用到二級緩存。開啟二級緩存后,會使用CachingExecutor裝飾Executor,進入一級緩存的查詢流程前,先在CachingExecutor進行二級緩存的查詢

二級緩存開啟后,同一個namespace下的所有操作語句,都影響著同一個Cache,即二級緩存被多個SqlSession共享,是一個全局的變量。 當開啟緩存后,數(shù)據(jù)的查詢執(zhí)行的流程就是 二級緩存 -> 一級緩存 -> 數(shù)據(jù)庫。

二級緩存(全局緩存):基于namespace級別的緩存,一個namespace對應一個二級緩存

###二級緩存配置

1、在MyBatis的配置文件中開啟二級緩存。

cacheEnabled 全局性地開啟或關(guān)閉所有映射器配置文件中已配置的任何緩存。

2、在MyBatis的映射XML中配置cache或者 cache-ref

  • type:cache使用的類型,默認是PerpetualCache,這在一級緩存中提到過。
  • eviction: 定義回收的策略,常見的有FIFO,LRU。
  • flushInterval: 配置一定時間自動刷新緩存,單位是毫秒。
  • size: 最多緩存對象的個數(shù)。
  • readOnly: 是否只讀,若配置可讀寫,則需要對應的實體類能夠序列化。
  • blocking: 若緩存中找不到對應的key,是否會一直blocking,直到有對應的數(shù)據(jù)進入緩存。
<mapper namespace="com.yan.dao.RoleMapper">
  <cache/>

cache-ref代表引用別的命名空間的Cache配置,兩個命名空間的操作使用的是同一個Cache。

二級緩存實驗

1、測試二級緩存效果,不提交事務,sqlSession1查詢完數(shù)據(jù)后,sqlSession2相同的查詢是否會從緩存中獲取數(shù)據(jù)。 可以看到,當sqlsession沒有調(diào)用commit()方法時,二級緩存并沒有起到作用。

SqlSession session1=MybatisSessionFactory.getFactory().openSession();
       SqlSession session2=MybatisSessionFactory.getSession();
       RoleMapper rm1=session1.getMapper(RoleMapper.class);
       RoleMapper rm2=session2.getMapper(RoleMapper.class);
       Role r1=rm1.selectByPrimaryKey(1L);
       session1.commit();  //如果不進行提交,則緩存無效
       Role r2=rm2.selectByPrimaryKey(1L);
        System.out.println(r1==r2);  //不是同一個對象,應該是對象的深克隆

2、測試二級緩存效果,當提交事務時,sqlSession1查詢完數(shù)據(jù)后,sqlSession2相同的查詢是否會從緩存中獲取數(shù)據(jù)。 sqlsession2的查詢,使用了緩存,緩存的命中率是0.5。

3、測試update操作是否會刷新該namespace下的二級緩存。 可以看到,在sqlSession3更新數(shù)據(jù)庫,并提交事務后,sqlsession2的StudentMapper namespace下的查詢走了數(shù)據(jù)庫,沒有走Cache。

4、驗證MyBatis的二級緩存不適應用于映射文件中存在多表查詢的情況。 通常我們會為每個單表創(chuàng)建單獨的映射文件,由于MyBatis的二級緩存是基于namespace的,多表查詢語句所在的namspace無法感應到其他namespace中的語句對多表查詢中涉及的表進行的修改,引發(fā)臟數(shù)據(jù)問題。

5、為了解決實驗4的問題呢,可以使用Cache ref,讓ClassMapper引用StudenMapper命名空間,這樣兩個映射文件對應的Sql操作都使用的是同一塊緩存了。 不過這樣做的后果是,緩存的粒度變粗了,多個Mapper namespace下的所有操作都會對緩存使用造成影響。

<mapper namespace="com.yan.dao.UserMapper">
   <cache-ref namespace="com.yan.dao.RoleMapper"/>

###二級緩存源碼

在一級緩存處理前,用CachingExecutor裝飾了BaseExecutor的子類,在委托具體職責給delegate之前,實現(xiàn)了二級緩存的查詢和寫入功能

CachingExecutor的query方法,首先會從MappedStatement中獲得在配置初始化時賦予的Cache。

本質(zhì)上是裝飾器模式的使用,具體的裝飾鏈是SynchronizedCache -> LoggingCache -> SerializedCache -> LruCache -> PerpetualCache。

  • SynchronizedCache: 同步Cache,實現(xiàn)比較簡單,直接使用synchronized修飾方法。
  • LoggingCache: 日志功能,裝飾類,用于記錄緩存的命中率,如果開啟了DEBUG模式,則會輸出命中率日志。SerializedCache: 序列化功能,將值序列化后存到緩存中。該功能用于緩存返回一份實例的Copy,用于保存線程安全。LruCache: 采用了Lru算法的Cache實現(xiàn),移除最近最少使用的key/value。
  • PerpetualCache: 作為為最基礎(chǔ)的緩存類,底層實現(xiàn)比較簡單,直接使用了HashMap。

總結(jié)

  • MyBatis的二級緩存相對于一級緩存來說,實現(xiàn)了SqlSession之間緩存數(shù)據(jù)的共享,同時粒度更加的細,能夠到namespace級別,通過Cache接口實現(xiàn)類不同的組合,對Cache的可控性也更強。
  • MyBatis在多表查詢時,極大可能會出現(xiàn)臟數(shù)據(jù),有設(shè)計上的缺陷,安全使用二級緩存的條件比較苛刻。一般在具體開發(fā)中不使用MyBatis的二級緩存,而是通過Spring框架引入業(yè)務層緩存
  • 在分布式環(huán)境下,由于默認的MyBatis Cache實現(xiàn)都是基于本地的,分布式環(huán)境下必然會出現(xiàn)讀取到臟數(shù)據(jù),需要使用集中式緩存將MyBatis的Cache接口實現(xiàn),有一定的開發(fā)成本,直接使用Redis,Memcached等分布式緩存可能成本更低,安全性也更高。

到此這篇關(guān)于Mybatis的延遲加載問題的文章就介紹到這了,更多相關(guān)Mybatis延遲加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • ElasticSearch不停機重建索引延伸思考及優(yōu)化詳解

    ElasticSearch不停機重建索引延伸思考及優(yōu)化詳解

    這篇文章主要為大家介紹了ElasticSearch不停機重建索引延伸思考及優(yōu)化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • Spring?Boot常用功能Profile詳解

    Spring?Boot常用功能Profile詳解

    SpringBootProfile是一個很常用的功能,我們可以通過為開發(fā)/測試/生產(chǎn)環(huán)境配置不同的profile來實現(xiàn)配置隔離,那么在SpringBoot項目中是如何實現(xiàn)profile功能的呢
    2022-07-07
  • java  hibernate使用注解來定義聯(lián)合主鍵

    java hibernate使用注解來定義聯(lián)合主鍵

    這篇文章主要介紹了java hibernate使用注解來定義聯(lián)合主鍵的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • 利用Java實現(xiàn)更改Word中的頁面大小和頁面方向

    利用Java實現(xiàn)更改Word中的頁面大小和頁面方向

    這篇文章主要為大家詳細介紹了一種高效便捷的方法——通過Java應用程序,以編程方式更改Word中的頁面大小和頁面方向,感興趣的可以了解一下
    2023-03-03
  • Java Web項目部署在Tomcat運行出錯與解決方法示例

    Java Web項目部署在Tomcat運行出錯與解決方法示例

    這篇文章主要介紹了Java Web項目部署在Tomcat運行出錯與解決方法,結(jié)合具體實例形式分析了Java Web項目部署在Tomcat過程中由于xml配置文件導致的錯誤問題常見提示與解決方法,需要的朋友可以參考下
    2017-03-03
  • JDK8安裝與配置實踐超詳細指南

    JDK8安裝與配置實踐超詳細指南

    本文詳細介紹了在Windows?64位系統(tǒng)上安裝和配置JDK8的步驟,包括JDK8下載、環(huán)境變量設(shè)置及安裝驗證,同時提供了JDK8新特性如Lambda表達式、StreamAPI等的概覽,旨在幫助Java開發(fā)者有效利用JDK8新特性進行開發(fā),需要的朋友可以參考下
    2024-10-10
  • springboot斷言異常封裝與統(tǒng)一異常處理實現(xiàn)代碼

    springboot斷言異常封裝與統(tǒng)一異常處理實現(xiàn)代碼

    異常處理其實一直都是項目開發(fā)中的大頭,但關(guān)注異常處理的人一直都特別少,下面這篇文章主要給大家介紹了關(guān)于springboot斷言異常封裝與統(tǒng)一異常處理的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Spring Boot整合Swagger測試api構(gòu)建全紀錄

    Spring Boot整合Swagger測試api構(gòu)建全紀錄

    這篇文章主要給大家介紹了關(guān)于Spring Boot整合Swagger測試api構(gòu)建的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-01-01
  • Java簡單數(shù)據(jù)加密方法DES實現(xiàn)過程解析

    Java簡單數(shù)據(jù)加密方法DES實現(xiàn)過程解析

    這篇文章主要介紹了Java簡單數(shù)據(jù)加密方法DES實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-12-12
  • SpringBoot集成Druid監(jiān)控頁面最小化配置操作

    SpringBoot集成Druid監(jiān)控頁面最小化配置操作

    這篇文章主要介紹了SpringBoot集成Druid監(jiān)控頁面最小化配置操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09

最新評論