Spring和mybatis整合后事務攔截器TransactionInterceptor開啟提交事務流程

目錄

  • 一、說明
  • 二、TransactionInterceptor開啟事務
    • (1)、攔截方法
    • (2)、開啟事務綁定數據庫連接
    • (3)、mybatis中sql執行數據庫連接獲取
    • (4)、事務提交和當前線程ThreadLocal清理,sqlSession關閉
  • 三、總結

一、說明

接著上一個博客SpringBoot 聲明式事務 源碼解析,下面看一下事務開啟后把當前數據庫連接綁定到ThreadLocal中,mybatis執行數據庫操作時,從ThreadLocal中獲取連接執行sql,最后攔截器提交或回滾事務,執行sqlsession(一個sqlsession對應一個數據庫連接)提交或回滾,然后清理ThreadLocal,關閉sqlsession,數據庫連接回收到數據庫連接池。

二、TransactionInterceptor開啟事務

(1)、攔截方法

	@Override@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}

執行invokeWithinTransaction方法,如下圖,1是獲取目標方法上配置的隔離級別和傳播屬性等屬性,2是開啟事務的具體方法,3是繼續執行后續攔截器最終執行目標方。
在這里插入圖片描述
在這里插入圖片描述

下面重點看2方法中的內容。

繼續看status = tm.getTransaction(txAttr);
在這里插入圖片描述

(2)、開啟事務綁定數據庫連接

下面跟蹤方法時,重點看開啟事務和綁定數據庫連接到TreadLocal中的內容。
在看代碼之前,先看看自動配置類中創建了DataSourceTransactionManager組件,不明白什么時候創建的可以看一下我上個博客。組件中放入了dataSource數據源,若依的框架中添加了動態數據源的配置。
在這里插入圖片描述
在這里插入圖片描述
下圖是status = tm.getTransaction(txAttr);方法中開啟事務的內容。doBegin方法時綁定數據庫連接到當前線程。doBegin下面prepareSynchronization里面也很重要,記錄一下往TransactionSynchronizationManager中設置了許多參數后面會用到,看一下 TransactionSynchronizationManager.initSynchronization();
在這里插入圖片描述
下圖第一個箭頭設置了ActualTransctionActive=true在提交事務的時候會用到。往synchronizations放入了空集合,synchronizations是一個ThreadLocal。
在這里插入圖片描述
在這里插入圖片描述
下面看一下doBegin方法。
在這里插入圖片描述
下面是從數據源中獲取連接,把連接設置到了txObject的ConnectionHolder數據庫連接描述對象中,后續還會從這里面取出。設置了newConnectionHolder=true.
在這里插入圖片描述
1、先判斷連接是不是自動提交,如果是自動提交會設置成不可以自動提交。2、把事務可用狀態設置成true,后續會用到。3、把當前連接信息綁定到當前線程。
在這里插入圖片描述
可以看到resources是ThreadLocal,ThreadLocal中放入了Map集合,key是動態數據源,value是數據庫連接描述對象ConnectionHolder。
在這里插入圖片描述
在這里插入圖片描述

(3)、mybatis中sql執行數據庫連接獲取

MybatisAutoConfiguration中會自動注入SqlSessionTemplate組件,@MapperScan中引入了ClassPathMapperScanner組件,組件掃描所有mapper.java文件,把接口設置成MapperFactoryBean類型的組件,可以一下我以前的博客Spring如何管理Mapper,在設置bean的描述時,也設置了SqlSessionTemplate組件到Mapper中,執行到Configuration.addMapper時,knownMappers.put(type, new MapperProxyFactory(type));往map中設置了MapperProxyFactory,當Configuration.getMapper時,會調用MapperProxyFactory生成代理類MapperProxy,默認使用的是jdk動態代理。綜上所述,當執行mapper的update方法時,會到MapperProxy代理方法invoke。后面會執行MapperMethod中執行execute方法。
在這里插入圖片描述
會執行SqlSessionTemplate中的update方法。如下圖當SqlSessionTemplate創建的時候,會設置SqlSessionTemplate的代理類sqlSessionProxy,SqlSessionInterceptor是代理類的攔截方法。執行SqlSessionTemplate中的update方法會進入SqlSessionInterceptor的invoke方法。

在這里插入圖片描述
從if判斷可以看到如果沒有開啟事務,sqlSession會提交事務。如果開啟了,使用事務攔截器統一提交事務。
在這里插入圖片描述
看一下if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory))內容,如果ThreadLocal中已經存入了sqlSession并和當前的sqlSession是同一個說明開啟了事務,使用事務攔截器提交。

在這里插入圖片描述

分析一下getSqlSession方法,主要是獲取sqlSession,先進入getSqlSession方法看ransactionSynchronizationManager.getResource(sessionFactory);

在這里插入圖片描述

從resources中獲取以sessionFactory為key,值是defaultSqlSession的map,其中resources是ThreadLocal。第一次執行map是null。
在這里插入圖片描述
請看源碼,我添加了注釋

  public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator) {notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);//從ThreadLocal中獲取SqlSessionHolder,可以通過SqlSessionHolder獲取生成的sqlSessionSqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);//從SqlSessionHolder中獲取sqlSession,如果獲取都會直接返回SqlSession session = sessionHolder(executorType, holder);if (session != null) {return session;}//通過sessionFactory和執行器類型創建sqlSessionLOGGER.debug(() -> "Creating a new SqlSession");session = sessionFactory.openSession(executorType);//把創建好的sqlSession,放入到ThreadLocal中,只有開啟事務才能放入registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);return session;}

看一下openSession代碼, tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,boolean autoCommit) {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);//1、創建了SpringManagedTransaction,傳入數據源參數tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);//2、創建執行器,傳入了SpringManagedTransactionfinal Executor executor = configuration.newExecutor(tx, execType);//3、創建DefaultSqlSession,傳入了執行器executor return createSqlSession(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();}}

會創建Transaction類型的組件SpringManagedTransaction,傳入了動態數據源。

 public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {return new SpringManagedTransaction(dataSource);}

繼續往下跟蹤到registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);代碼如下在這里插入圖片描述
1、里面代碼判斷synchronizations是不是null,這里上面提到過,在TranscationIntercetor中開啟事務后,prepareSynchronization方法中設置了空集合,所以這里是TransactionSynchronizationManager.isSynchronizationActive()=true,synchronizations也是TreadLocal避免了線程不安全問題。

在這里插入圖片描述

2、創建了SqlSessionHolder其中包含創建好的DefaultSqlSession。
3、往ThreadLocal中放入了Map,map的key是sessionFactory,value是2中創建的SqlSessionHolder。開啟事務后,執行后面的sql可以從ThreadLocal取出。
在這里插入圖片描述

4、創建了SqlSessionSynchronization其中包含創建好的sqlsession,放入到了集合中,此集合會放入1中synchronizations中,后面提交事務的時候會用到。
在這里插入圖片描述
5、設置了SqlSessionHolder中SynchronizedWithTransaction=true。
總上所述,當開啟事務后,在同一個事務中,使用mybatis執行多個sql時,會重復使用同一個DefaultSqlSession,(DefaultSqlSession被綁定到了線程中),也會使用同一個數據庫連接,保證可以使用事務。如果沒有開啟事務,每次執行sql都會重新創建一個DefaultSqlSession。事務的開啟和提交回滾都是mybatis來負責的。

繼續回到MapperMethod.execute->sqlSession.update(command.getName(), param)
->executor.update(ms, wrapCollection(parameter));
->BaseExecutor.update
->SimpleExecutor.doUpdate
->prepareStatement(handler, ms.getStatementLog());
->Connection connection = getConnection(statementLog);–>openConnection();
-> this.connection = DataSourceUtils.getConnection(this.dataSource);
->doGetConnection 如下代碼可以看到從ThreadLocal里面獲取map使用動態數據源做key,獲取數據庫連接描述對象,這個數據庫連接對象在開啟事務后放入的,可以找找上面的內容有提到。SpringManagedTransaction類中
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);

public static Connection doGetConnection(DataSource dataSource) throws SQLException {Assert.notNull(dataSource, "No DataSource specified");ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {conHolder.requested();if (!conHolder.hasConnection()) {logger.debug("Fetching resumed JDBC Connection from DataSource");conHolder.setConnection(fetchConnection(dataSource));}return conHolder.getConnection();}// Else we either got no holder or an empty thread-bound holder here.logger.debug("Fetching JDBC Connection from DataSource");Connection con = fetchConnection(dataSource);

在這里插入圖片描述
獲取數據庫連接后會設置到SpringManagedTransaction類中的connection屬性中,下面看一下SpringManagedTransaction類關系。

在這里插入圖片描述
每次創建sqlsession的時候會先都會創建SpringManagedTransaction,SpringManagedTransaction當獲取數據庫連接后會設置到本類的屬性connection上,創建執行器Excutor是會把SpringManagedTransaction設置進去,然后把Excutor設置到sqlsession中。這可以理解為同一個sqlsession對應同一個數據庫連接java.sql.Connection。
在這里插入圖片描述

(4)、事務提交和當前線程ThreadLocal清理,sqlSession關閉

定位到TransactionAspectSupport.invokeWithinTransaction方法,方法內開啟事務,執行攔截器和目標方法最后提交事務。下面看看提交事務方法。
commitTransactionAfterReturning(txInfo);
在這里插入圖片描述

txInfo.getTransactionManager().commit(txInfo.getTransactionStatus())
->processCommit(defStatus);
->triggerBeforeCompletion
-> TransactionSynchronizationUtils.triggerBeforeCompletion();
遍歷TransactionSynchronization執行beforeCompletion

	for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {try {synchronization.beforeCompletion();}catch (Throwable tsex) {logger.error("TransactionSynchronization.beforeCompletion threw exception", tsex);}}
  public void beforeCompletion() {// Issue #18 Close SqlSession and deregister it now// because afterCompletion may be called from a different threadif (!this.holder.isOpen()) {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Transaction synchronization deregistering SqlSession [" + this.holder.getSqlSession() + "]");}//刪除ThreadLocal中綁定的sessionFactory,和sqlSession
TransactionSynchronizationManager.unbindResource(sessionFactory);this.holderActive = false;if (LOGGER.isDebugEnabled()) {LOGGER.debug("Transaction synchronization closing SqlSession [" + this.holder.getSqlSession() + "]");}//關閉sqlSessionthis.holder.getSqlSession().close();}}

主要看一下 TransactionSynchronizationManager.unbindResource(sessionFactory);清理綁定的sessionFactory和sqlSession的map集合

public static Object unbindResource(Object key) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Object value = doUnbindResource(actualKey);if (value == null) {throw new IllegalStateException("No value for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}return value;}
	private static Object doUnbindResource(Object actualKey) {Map<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {resources.remove();}// Transparently suppress a ResourceHolder that was marked as void...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {value = null;}if (value != null && logger.isTraceEnabled()) {logger.trace("Removed value [" + value + "] for key [" + actualKey + "] from thread [" +Thread.currentThread().getName() + "]");}return value;}

最終提交事務,方法在processCommit->triggerBeforeCommit->TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly())遍歷所有的TransactionSynchronization 類型的組件前面添加過SqlSessionSynchronization其中包含了創建好的sqlsession

public static void triggerBeforeCommit(boolean readOnly) {for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {synchronization.beforeCommit(readOnly);}}

獲取當前線程的sqlsession,執行提交操作。可以看一下if條件,就是開啟事務后在prepareSynchronization方法中設置的ThreadLocal類型的屬性actualTransactionActive中是true。

在這里插入圖片描述
執行到sqlsession中執行器的commit,設置不能自動提交。
在這里插入圖片描述
繼續執行執行器中的SpringManagedTransaction中的commit,SpringManagedTransaction在創建sqlsession的時候提到了,
在這里插入圖片描述
最終獲取SpringManagedTransaction中的connection,進行事務提交。
在這里插入圖片描述

三、總結

1、TransactionInterceptor攔截到目標方法開啟事務設置第一個ThreadLocal放入數據源為key,數據庫連接描述類為value的map集合。
2、執行mybatis的sql時,sqlSession中的excutor中SpringManagedTransaction類會從第一個ThreadLocal中根據動態數據源取出相應的數據庫連接執行sql,保證了開啟的事務和執行sql同一個數據庫連接。
3、在mybatis的sqlSessionTemplate執行增刪改方法時,sqlSession的代理類SqlSession執行getSqlSession,如果開啟了事務,出現第二個ThreadLocal,里面存放以sqlSessionFactory為key,defaultSqlSession為value的map集合,如何在同一個事務中,執行每個sql,defaultSqlSession會使用同一個。如果沒有開啟事務第二個ThreadLocal不生效,每次執行sql都會創建一次defaultSqlSession。事務的開啟和提交都是mybatis控制的。
為何開啟事務后,在事務中每個mapper增刪改查操作都使用同一個sqlsession呢?因為 MyBatis 的 SqlSession 在設計上就是數據庫連接(java.sql.Connection)的一個高級封裝和門面(Facade)對象。一個 SqlSession 實例在其生命周期內,內部始終持有且僅持有一個 Connection 對象,同一個 SqlSession 就是同一個數據庫連接,提交事務時,多個方法使用同一個SqlSession提交方法進而同一個數據庫連接提交,保證了事務一致性
一個 SqlSession 實例 → 包含一個 Executor 實例 → 持有一個 Transaction 對象 → 管理一個唯一的 Connection 對象。
4、當事務提交成功或回滾時,會自動清理掉兩個ThreadLocal中當前線程中的數據關閉SqlSession,回收 Connection 對象到線程池。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/95135.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/95135.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/95135.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

05.《ARP協議基礎知識探秘》

ARP協議基本介紹與實踐 文章目錄**ARP協議基本介紹與實踐**ARP概述ARP報文類型ARP工作過程解析ARP工作原理示意圖無故ARP/免費ARP實驗案例**實驗目標**實驗環境實驗步驟ARP概述 作用&#xff1a;ARP&#xff08;Address Resolution Protocol&#xff0c;地址解析協議&#xff…

互聯網大廠面試:大模型應用開發崗位核心技術點解析

互聯網大廠面試&#xff1a;大模型應用開發崗位核心技術點解析 第一輪&#xff1a;大模型基礎與上下文工程 問題 1&#xff1a;你能簡單介紹 Transformer 架構的工作原理嗎&#xff1f; 小C&#xff1a;嗯&#xff0c;我理解是 Transformer 主要依賴自注意力機制&#xff08;Se…

【深度學習新浪潮】有沒有什么方法可以將照片變成線描稿,比如日式漫畫的那種?

一、技術原理與研究進展 1. 線描生成的核心技術路徑 傳統方法:基于邊緣檢測(如Canny算子)和形態學操作,但難以處理復雜紋理和藝術風格。 深度學習方法: 端到端生成:使用U-Net架構(如ArtLine項目)直接學習照片到線描的映射,結合自注意力機制和感知損失提升細節保留能力…

NV032NV037美光固態閃存NV043NV045

NV032NV037美光固態閃存NV043NV045在數字化浪潮席卷全球的當下&#xff0c;存儲技術的每一次突破都深刻影響著從個人消費到企業級應用的各個領域。美光科技作為行業領軍者&#xff0c;其NV系列固態閃存產品始終以技術創新為核心驅動力。本文將聚焦NV032、NV037、NV043、NV045四…

天碩G40工業固態硬盤破解軌道存儲難題

在高鐵與軌道交通高速發展的今天&#xff0c;軌道檢測探傷是保障列車安全運行的核心環節。據統計&#xff0c;我國鐵路總里程已突破16萬公里&#xff0c;日均檢測數據量超10TB。加固平板一體機作為軌道探傷領域的“移動工作站”&#xff0c;需要在跨越大江南北的極端環境中實時…

基于Velero + 阿里云 OSS的Kubernetes 集群的備份與恢復

在 Kubernetes&#xff08;K8s&#xff09;中&#xff0c;備份和恢復是保障數據安全與業務連續性的關鍵環節&#xff0c;主要方式包括 ETCD 備份恢復 和 Velero 備份恢復&#xff0c;兩者在備份粒度、恢復影響范圍、存儲位置等方面存在以下差異&#xff1a; 1、ETCD 備份恢復&…

解構與重構:“真人不露相,露相非真人” 的存在論新解 —— 論 “真在” 的行為表達本質

解構與重構&#xff1a;“真人不露相&#xff0c;露相非真人” 的存在論新解 —— 論 “真在” 的行為表達本質緒論&#xff1a;傳統解釋的突圍 —— 從 “藏才” 到 “存真”“真人不露相&#xff0c;露相非真人” 這句諺語&#xff0c;自明代《西游記》以降&#xff0c;便長期…

數據結構:哈希表、排序和查找

一、哈希算法1.將數據通過哈希算法映射成一個健值&#xff0c;存取都在同一個位置&#xff0c;實現數據的高效存儲和查找&#xff0c;時間復雜度由O(n)->O(1)2.哈希碰撞&#xff1a;多個數據通過哈希算法得到的鍵值相同二、哈希表1.構建哈希表存放0-100之間的數據2.哈希算法…

【Java基礎】Java I/O模型解析:BIO、NIO、AIO的區別與聯系(Netty入門必備基礎)

Java I/O模型深度解析&#xff1a;BIO、NIO、AIO的區別與聯系 引言 在Java的網絡編程與文件操作中&#xff0c;I/O&#xff08;輸入/輸出&#xff09;模型是繞不開的核心話題。從早期的BIO&#xff08;Blocking I/O&#xff09;到Java 1.4引入的NIO&#xff08;Non-blocking I/…

windows PowerToys之無界鼠標:一套鍵鼠控制多臺設備

&#x1f4bb;簡介 在多設備協作的工作場景中&#xff0c;如何實現一套鍵鼠控制多臺設備了&#xff1f;微軟推出的 PowerToys 工具集中的 Mouse Without Borders&#xff08;無界鼠標&#xff09;&#xff0c;通過軟件層實現跨設備的鍵鼠共享與數據同步功能&#xff0c;為多臺…

一道比較難的sql題,篩選出重復字段的行數

select * from 導入數據表; id city_column 1 北京,上海,廣州 2 上海,上海,深圳 3 北京,杭州,北京 4 上海,廣州,深圳select substring_index(khmc,,,1), * from 導入數據表 truncate table 導入數據表 select count(distinct khmc) from 導入數據表; …

【K8s】整體認識K8s之與集群外部訪問--service

這一篇文章主要是對service發現新的理解 為什么要使用service服務發現&#xff1f; 首先pod的IP&#xff0c;是動態的&#xff0c;當我們重啟一個pod的時候&#xff0c;它會給它分配一個新的IP&#xff0c;但是如果微服務a想要去調用微服務b&#xff0c;他是需要知道微服務b所有…

k8s(自寫)

kubernetes k8s是什么&#xff1f;Kubernetes是什么&#xff1f;架構是怎么樣的&#xff1f;6分鐘快速入門_嗶哩嗶哩_bilibili kubernetes是google開源神器&#xff0c;介于應用服務和服務器之間&#xff0c;能夠通過策略協調和管理多個應用服務&#xff0c;只需要一個yaml文…

實現微信小程序的UniApp相機組件:拍照、錄像與雙指縮放

在微信小程序開發中&#xff0c;相機功能已成為許多應用的核心組成部分。本文將介紹如何使用UniApp框架實現一個功能豐富的相機組件&#xff0c;支持拍照、錄像、前后攝像頭切換以及雙指縮放等功能。功能概述這個相機組件具備以下核心功能&#xff1a;拍照功能&#xff1a;支持…

python pyqt5開發DoIP上位機【診斷回復的函數都是怎么調用的?】

目錄 文章合集 一、底層網絡接收:`_receive_loop`(觸發起點) 調用時機: 核心代碼: 作用: 二、數據解析:`handle_received_data`(判斷是否為診斷回復) 調用時機: 核心代碼(診斷回復相關部分): 作用: 三、UI顯示:`add_trace_entry`(展示到界面) 調用時機: 信號…

談物質的運動與運動的物質

運動的物質是不是物質的運動&#xff0c;如果假設是&#xff08;第一假設&#xff09;&#xff0c;那末運動的物質是物質的運動&#xff0c;而運動是物質的根本屬性&#xff0c;又運動的物質是物質&#xff0c;則物質的運動是物質&#xff0c;既然運動是物質的根本屬性&#xf…

【MLLM】多模態理解Ovis2.5模型架構和訓練流程

note 模型架構&#xff1a;延續 Ovis 系列創新的結構化嵌入對齊設計。 Ovis2.5 由三大組件構成&#xff1a;動態分辨率 ViT 高效提取視覺特征&#xff0c;Ovis 視覺詞表模塊實現視覺與文本嵌入的結構對齊&#xff0c;最后由強大的 Qwen3 作為語言基座&#xff0c;處理多模態嵌…

3.3單鏈表專題

順序表這種在標準庫已經實現好了&#xff0c;直接調用 pushback pushfront 這些o(1)表示不額外開辟空間src為value繼續走&#xff0c;下一個不是value&#xff0c;src值給dst空間&#xff0c;dst&#xff0c;dst剛好等于2&#xff0c;就是新數組長度。若從前向后兩個數組元素依…

linux系統學習(15.啟動管理)

目錄 一、運行級別 1.運行級別 2.運行級別命令 (1)runlevel (2)init 運行級別 3.永久修改啟動級別&#xff08;ubantu20.04&#xff09; 二、啟動過程 &#x1f539; 總結 三、啟動引導程序grub配置文件 一、運行級別 1.運行級別 2.運行級別命令 (1)runlevel (2)ini…

檢索優化-混合檢索

混合檢索&#xff08;Hybrid Search&#xff09;是一種結合了 稀疏向量&#xff08;Sparse Vectors&#xff09; 和 密集向量&#xff08;Dense Vectors&#xff09; 優勢的先進搜索技術。旨在同時利用稀疏向量的關鍵詞精確匹配能力和密集向量的語義理解能力&#xff0c;以克服…