最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis的SqlSession和一級緩存的失效原因分析及解決

 更新時間:2025年05月22日 09:21:26   作者:Linn-cn  
這篇文章主要介紹了Mybatis的SqlSession和一級緩存的失效原因分析及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

SqlSession解讀

SqlSession是什么?

SqlSession是Mybatis 中定義的,用來表示與關(guān)系數(shù)據(jù)庫的一次會話,會話定義了各種具體的操作,查詢、數(shù)據(jù)更新(包含保存、更新、刪除)操作。而這些操作都在與數(shù)據(jù)庫建立會話的基礎(chǔ)上進行的。

SqlSession 可以看作是對Connection 更加高級的抽象,從其方法上更加可以看出他具有更加明顯的操作特征。

SqlSession分類

mybatis的SqlSession有三種:

DefaultSqlSession、SqlSessionManager、SqlSessionTemplate,前兩者是mybtais默認情況下使用的,第三種主要用到mybatis和spring整合的時候。

SqlSession的創(chuàng)建

那么SqlSession 是如何被創(chuàng)建的?

在學(xué)習(xí)Mybatis時,我們常??吹降?SqlSession 創(chuàng)建方式是 SqlSessionFactory.openSession() ,那么我們就從它作為切入點,先來看看 SqlSessionFactory.openSession() 的方法源碼(需要注意的是這里是實現(xiàn)類DefaultSqlSessionFactory )

代碼如下:

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 獲取環(huán)境配置
      final Environment environment = configuration.getEnvironment();
      // 創(chuàng)建事務(wù)
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 創(chuàng)建執(zhí)行器
      final Executor executor = configuration.newExecutor(tx, execType);
      // 創(chuàng)建sqlsession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

通過源碼我們知道每次 SqlSession(準確地說是 DefaultSqlSession )的創(chuàng)建都會有一個 Transaction 事務(wù)對象 的生成。也就是說:

  • 一個事務(wù) Transaction 對象與一個 SqlSession 對象 是一一對應(yīng)的關(guān)系。
  • 同一個SqlSession 不管執(zhí)行多少次數(shù)據(jù)庫操作。只要沒有執(zhí)行close,那么整個操作都是在同一個 Transaction 中執(zhí)行的。

但需要注意的是,我們整合Spring之后用到的其實都是 SqlSessionTemplate ,與這里的 DefaultSqlSession 不是同一個SqlSession對象,不懂的看上面的。

為什么和Spring整合后的SqlSession一級緩存偶爾會失效?

我們都知道m(xù)ybatis有一級緩存和二級緩存。一級緩存是SqlSession級別的緩存,在操作數(shù)據(jù)庫時,每個SqlSession類的實例對象緩存的數(shù)據(jù)區(qū)域(Map)可以用于存儲緩存數(shù)據(jù),不同的SqlSession類的實例對象緩存的數(shù)據(jù)區(qū)域是互不影響的。 

  • 一級緩存工作原理圖:

二級緩存是Mapper級別的緩存,多個SqlSession實例對象可以共用二級緩存,二級緩存是跨SqlSession的。 

  • Mybatis緩存模式圖如下:

我們知道在和Mybatis和Spring的整合中不管是創(chuàng)建MapperProxy 的 SqlSession 還是 MapperMethod中調(diào)用的SqlSession其實都是** SqlSessionTemplate **。

SqlSessionTemplate的神秘面紗

如果你閱讀了上面的鏈接文章,就知道 每創(chuàng)建一個 MapperFactoryBean 就會創(chuàng)建一個 SqlSessionTemplate 對象,而 MapperFactoryBean 在獲取 MapperProxy 時會將 SqlSessionTemplate 傳遞到 MapperProxy中。 也就是說 SqlSessionTemplate 的生命周期是與 MapperProxy 的生命周期是一致的。

SqlSessionTemplate 內(nèi)部維護了一個 sqlSessionProxy ,而 sqlSessionProxy 是通過動態(tài)代理創(chuàng)建的一個 SqlSession 對象, SqlSessionTemplate 的 數(shù)據(jù)庫操作方法 insert/update 等等都是委托 sqlSessionProxy 來執(zhí)行的,我們看一下它的構(gòu)造方法:

// 構(gòu)造方法
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
                          PersistenceExceptionTranslator exceptionTranslator) {
    ...省略無關(guān)緊要的代碼
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
}

我們會發(fā)現(xiàn)這個類也繼承了SqlSession接口,我們選擇一個查詢方法來深入看一下為什么mybatis一級緩存偶爾會失效,我們進入到他的selectList方法,看下他的實現(xiàn)邏輯:

public class SqlSessionTemplate implements SqlSession, DisposableBean {...}

@Override
public <E> List<E> selectList(String statement, Object parameter) {
    return this.sqlSessionProxy.selectList(statement, parameter);
}

我們發(fā)現(xiàn),這個方法內(nèi)部內(nèi)部的查詢又交給了一層代理,由這一層代理去真正執(zhí)行的查詢操作,而這個代理就是在SqlSessionTemplate創(chuàng)建的時候進行設(shè)置的。

如果熟悉動態(tài)代理的話,就知道,我們接下來需要看的就是SqlSessionInterceptor,我們進入到里面看一下他的實現(xiàn):

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      // 去獲取SqlSession
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
        // 通過反射調(diào)用真正的處理方法
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // 提交數(shù)據(jù)
          sqlSession.commit(true);
        }
        // 返回查詢的數(shù)據(jù)
        return result;
      } catch (Throwable t) {
        ...省略無關(guān)緊要的代碼
      } finally {
        if (sqlSession != null) {
          // 關(guān)閉SqlSession的連接
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

整個 invoke 分5個步驟:

  • 根據(jù)條件獲取一個SqlSession(注意此時的SqlSession 是 DefaultSqlSession ),此時的SqlSession 可能是新創(chuàng)建的,也可能是上一次的請求的SqlSession。
  • 反射執(zhí)行 SqlSession 方法
  • 判斷當(dāng)前的 SqlSession 是否由事務(wù)所管控,如果是則不commit
  • 判斷如果是PersistenceExceptionTranslator且不為空,那么就關(guān)閉當(dāng)前會話,并且將sqlSession置為空防止finally重復(fù)關(guān)閉
  • 只要當(dāng)前會話不為空, 那么就會關(guān)閉當(dāng)前會話操作,關(guān)閉當(dāng)前會話操作又會根據(jù)當(dāng)前會話是否有事務(wù)來決定會話是釋放還是直接關(guān)閉。

我們都知道一級緩存是SqlSession級別的緩存,那么一級緩存失效,肯定是因為SqlSession不一致,那么我們進入到getSqlSession方法中:

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {
    ...省略無關(guān)緊要的代碼
    // 從ThreadLocal變量里面獲取到Spring的事務(wù)同步管理器
    SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    // 調(diào)用靜態(tài)方法sessionHoler 判斷是否存在符合要求的sqlSession
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
      return session;
    }
    // 如果SqlSessionHolder中獲取的SqlSession為空,則新建一個SqlSession
    session = sessionFactory.openSession(executorType);
    // 判斷當(dāng)前是否存在事務(wù),將sqlSession 綁定到sqlSessionHolder 中,并放到threadLoacl 當(dāng)中
    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
    return session;
  }

看到這,我們應(yīng)該知道為什么Spring和MyBatis整合后,偶爾會一級緩存失效了,是因為Spring只有在開啟了事務(wù)之后,在同一個事務(wù)里的SqlSession會被緩存起來,同一個事務(wù)中,多次查詢是可以命中緩存的!

SqlSessionInterceptor#invoke方法里面,他在關(guān)閉的SqlSession的時候同樣對是否開啟事務(wù)做了處理,感興趣的可以看closeSqlSession方法的源碼:

public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
	...省略無關(guān)緊要的代碼
    SqlSessionHolder holder = 
          (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    // 查看事務(wù)同步管理器是否存在 session 
    if ((holder != null) && (holder.getSqlSession() == session)) {
      holder.released();
    } else {
      // 如果不存在就將該Session關(guān)閉掉
      session.close();
    }
  }

總結(jié)

  • 同一事務(wù)中不管調(diào)用多少次 mapper里的方法 ,最終都是用得同一個sqlSession,即一個事務(wù)中使用的是同一個sqlSession。
  • 同一事務(wù)中,Mybatis的一級緩存才會有效。
  • 如果沒有開啟事務(wù),調(diào)用一次mapper里的方法將會新建一個sqlSession來執(zhí)行方法。

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • IDEA解決springboot熱部署失效問題(推薦)

    IDEA解決springboot熱部署失效問題(推薦)

    熱部署,就是在應(yīng)用正在運行的時候升級軟件,卻不需要重新啟動應(yīng)用。這篇文章主要介紹了IDEA解決springboot熱部署失效問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • 淺談SpringCloud之Ribbon詳解

    淺談SpringCloud之Ribbon詳解

    這篇文章主要介紹了淺談SpringCloud之Ribbon,文中有非常詳細的代碼示例,對正在學(xué)習(xí)SpringCloud的小伙伴們有很大的幫助,需要的朋友可以參考下
    2021-05-05
  • 深入理解java虛擬機的故障處理工具

    深入理解java虛擬機的故障處理工具

    大家都知道在給系統(tǒng)定位問題的時候,知識、經(jīng)驗是關(guān)鍵基礎(chǔ),數(shù)據(jù)是依據(jù),工具是運用知識處理數(shù)據(jù)的手段。Java開發(fā)人員可以在jdk安裝的bin目錄下找到除了java,javac以外的其他命令。這些命令主要是一些用于監(jiān)視虛擬機和故障處理的工具,下面來看看詳細的介紹。
    2016-11-11
  • Spring中filter過濾器的定義方法

    Spring中filter過濾器的定義方法

    這篇文章主要介紹了Spring中filter過濾器的定義方法,Filter 程序是一個實現(xiàn)了特殊接口的 Java 類,與 Servlet 類似,也是由 Servlet 容器進行調(diào)用和執(zhí)行的,需要的朋友可以參考下
    2023-08-08
  • JAVA?拷貝文件的幾種方式小結(jié)

    JAVA?拷貝文件的幾種方式小結(jié)

    本文主要介紹了JAVA拷貝文件的幾種方式,包含普通拷貝,mmap內(nèi)存映射的方式拷貝,零拷貝sendFile方式實現(xiàn)和多線程的方式實現(xiàn)拷貝,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • Spring Boot攔截器Interceptor與過濾器Filter詳細教程(示例詳解)

    Spring Boot攔截器Interceptor與過濾器Filter詳細教程(示例詳解)

    本文詳細介紹了SpringBoot中的攔截器(Interceptor)和過濾器(Filter),包括它們的定義、作用范圍、使用場景、實現(xiàn)步驟、執(zhí)行順序、常見問題及解決方案,感興趣的朋友跟隨小編一起看看吧
    2025-03-03
  • 如何用Java?幾分鐘處理完?30?億個數(shù)據(jù)(項目難題)

    如何用Java?幾分鐘處理完?30?億個數(shù)據(jù)(項目難題)

    現(xiàn)有一個 10G 文件的數(shù)據(jù),里面包含了 18-70 之間的整數(shù),分別表示 18-70 歲的人群數(shù)量統(tǒng)計,今天小編通過本文給大家講解如何用Java?幾分鐘處理完?30?億個數(shù)據(jù),這個問題一直以來是項目難題,今天通過本文給大家詳細介紹下,感興趣的朋友一起看看吧
    2022-07-07
  • Java 集合框架高級應(yīng)用與架構(gòu)設(shè)計方案

    Java 集合框架高級應(yīng)用與架構(gòu)設(shè)計方案

    文章主要介紹了Java集合框架在復(fù)雜架構(gòu)場景中的高級應(yīng)用,包括緩存設(shè)計、分層存儲、數(shù)據(jù)分片、設(shè)計模式融合實踐、大數(shù)據(jù)量處理以及分布式場景中集合的適配問題和優(yōu)化方案,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • Dubbo在Spring和Spring Boot中的使用詳解

    Dubbo在Spring和Spring Boot中的使用詳解

    這篇文章主要介紹了Dubbo在Spring和Spring Boot中的使用詳解,需要的朋友可以參考下
    2017-10-10
  • Java中Stream流的使用示例詳解

    Java中Stream流的使用示例詳解

    Stream流提供了一種高效且易于使用的方式來對數(shù)據(jù)進行過濾、映射、排序、聚合等操作,使代碼更加簡潔和可讀,接下來通過本文給大家介紹Java中Stream流的使用,感興趣的朋友跟隨小編一起看看吧
    2025-08-08

最新評論

汕头市| 洪江市| 三穗县| 凤台县| 南漳县| 古蔺县| 陇川县| 龙岩市| 犍为县| 巴林右旗| 汝南县| 邵武市| 资中县| 武陟县| 永善县| 荆门市| 顺昌县| 长丰县| 泰州市| 朔州市| 同江市| 岚皋县| 永昌县| 淮滨县| 宜昌市| 红桥区| 万源市| 全南县| 柳河县| 峨眉山市| 贵溪市| 海原县| 保亭| 和田县| 蚌埠市| 渭源县| 南阳市| 镇安县| 河津市| 兴宁市| 阳高县|