Seata 無侵入式分散式事務服務的實現基石-JDBC篇

張哥說技術發表於2023-03-06


背景

Seata 是一款開源的分散式事務解決方案,致力於提供高效能和簡單易用的分散式事務服務 若把經典的 TCC 模式類比成手動擋駕駛模式,麼 Seata 的 AT 模式就好比自動擋駕駛模式,分散式事務這個強大且複雜的服務能力由 Seata 框架託管,對業務實現無侵入式,使用者仍然只專注於業務 SQL 


Seata 無侵入式分散式事務服務的實現基石-JDBC篇


使用者只需配置下資料來源代理,在需要全域性事務的方法上新增註解即可,就是這麼簡單。AT 模式是基於 DataSource 代理實現的,透過代理 DataSource ,攔截 SQL 執行,增強其執行邏輯,由代理側加入額外的能力以提供分散式事務服務(好似鋼鐵俠的機甲提供了超強能力)。

Seata 無侵入式分散式事務服務的實現基石-JDBC篇

從下圖中可看出,資料來源代理的實現涉及到 DataSource、Connection 以及 Statement,這幾個關鍵知識屬於 JDBC 的範疇,所以本篇從 JDBC 的視角對他們進行介紹。AT 模式維度的更多內容可檢視關於關於用 Seata 搞定分散式事務的規範化建設-賦能產研|提質增效

Seata 無侵入式分散式事務服務的實現基石-JDBC篇

一、JDBC 概述

JDBC 代表 Java 資料庫連線。JDBC 是一種 Java API,用於連線資料庫並執行查詢。它是 JavaSE(Java 標準版)的一部分。JDBC API 使用 JDBC 驅動程式連線資料庫。

Seata 無侵入式分散式事務服務的實現基石-JDBC篇

在 JDBC 之前,ODBC API 是連線資料庫並執行查詢的資料庫 API。但是,ODBC API 使用用 C 語言編寫的 ODBC 驅動程式(即依賴於平臺且不安全)。這就是為什麼 Java 定義了自己的 API (JDBC API),它使用 JDBC 驅動程式(用 Java 語言編寫)。當前的 JDBC 基於 X/Open SQL 呼叫級別介面。java.sql包包含 JDBC API 的類和介面。下面給出了 JDBC API 的流行介面列表:

  • Driver interface
  • Connection interface
  • Statement interface
  • PreparedStatement interface
  • CallableStatement interface
  • ResultSet interface
  • ResultSetMetaData interface
  • DatabaseMetaData interface
  • RowSet interface

我們可以使用 JDBC API 來使用 Java 程式處理資料庫,使用 JDBC 運算元據源大致需要以下幾個步驟:

  • 與資料來源建立連線。
  • 執行 SQL 語句,檢索 SQL 執行結果
  • 關閉連線。

二、與資料來源建立連結

Connection 是 JDBC 對資料來源連線的抽象,一旦建立了連線,使用 JDBC API 的應用程式就可以對目標資料來源執行查詢和更新操作。

Seata 無侵入式分散式事務服務的實現基石-JDBC篇

獲取Connection有兩種途徑

2.1 DriverManager

這是一個在 JDBC 1.0 規範中就已經存在、完全由 JDBC API 實現的驅動管理類。MYSQL5 之前需要Class.forName(“com.mysql.cj.jdbc.Driver”)的方式主動註冊驅動。MYSQL5 之後的驅動包可以省略註冊驅動的步驟,會自動載入 jar 包中 META-INF/services/java.sql.Driver 檔案中的 JDBC 驅動類。透過getConnection獲取資料庫連線,如下:

Connection conn = DriverManager.getConnection(url,username,password);

2.2 DataSource:

DataSource 介面是 JDBC 2.0 API 中的新增內容,它提供了連線到資料來源的另一種方法。使用 DataSource 物件是連線到資料來源的首選方法。需要注意 JDBC API 中只提供了 DataSource 介面,DataSource 具體的實現由 JDBC 驅動程式提供。JDBC API 中定義了兩個 DataSource 介面比較重要的擴充套件,用於支撐企業級應用。這兩個介面分別為:

  • ConnectionPoolDataSource   * 支援快取和複用Connection物件,主流的資料庫連線池也提供了DataSource介面的具體實現,如Druid提供了 DruidDataSource,生產中我們會使用資料庫連線池所提供的池化的Connection。連線池透過對連線的複用,而不是每次需要運算元據源時都新建一個物理連線來顯著地提高程式的效率,這樣能夠在很大程度上提升應用效能和伸縮性Seata 無侵入式分散式事務服務的實現基石-JDBC篇
  • XADataSource * 該例項返回的 Connection 物件能夠支援分散式事務;如 Druid 中會提供DruidXADataSource。XAConnection 介面繼承了 PooledConnection 介面,因此它具有所有 PooledConnection 的特性Seata 無侵入式分散式事務服務的實現基石-JDBC篇

三、執行 sql、檢索結果

3.1 建立Statement

獲取到 JDBC 中的Connection物件之後,我們可以透過Connection物件設定事務屬性,並且可以透過Connection介面中提供的方法建立StatementPreparedStatement或者CallableStatement物件。如:

java.sql.Connection#createStatement()

PreparedStatement和CallableStatement是Statement的子介面,Statement介面中定義了執行SQL語句的方法,但這些方法不支援引數輸入。

  • PreparedStatement

    • 介面繼承自Statement介面,增加了引數佔位符功能,當執行SQL語句時,可使用“?”作為引數佔位符,然後使用其提供的其他方法為佔位符設定引數值。其例項物件包含已編譯的 SQL 語句,由於已預編譯過,所以其執行速度要快於 Statement 物件。因此,多次執行的 SQL 語句經常建立為 PreparedStatement 物件,以提高效率。(這裡挖個坑,因為其引數設定機制,在實踐中也可能會遇到其帶來的問題)
  • CallableStatement

    • 介面繼承自PreparedStatement介面,在PreparedStatement的基礎上增加了呼叫儲存過程並檢索呼叫結果的功能。

3.2 執行 sql

Statement介面可以理解為 JDBC API 中提供的 SQL 語句的執行器,我們呼叫Statement介面中定義的不同方法以實現不同的結果:

  • 呼叫 executeQuery()方法執行查詢操作
  • 呼叫 executeUpdate()方法執行更新操作
  • 呼叫 executeBatch()方法執行批次處理

3.3 獲取結果

對結果的處理則:

  • 透過 getResultSet()方法來獲取查詢結果集,ResultSet 物件代表查詢操作的結果集
  • 透過 ResultSet 物件的 getMetaData()方法獲取結果集後設資料資訊,該方法返回一個 ResultSetMetaData 物件,我們可以透過 ResultSetMetaData 物件獲取結果集中所有的欄位名稱、欄位數量、欄位資料型別等資訊
  • 透過 getUpdateCount()方法來獲取更新操作影響的行數

3.4 Connection、Statement、ResultSet 之間的關係

Seata 無侵入式分散式事務服務的實現基石-JDBC篇

四 關閉 Connection 物件

當使用完 Connection 物件後,需要顯式地關閉該物件。Connection 中的 close()方法用於關閉 Connection 物件,由該 Connection 物件建立的所有 Statement 物件也都會被關閉。連線池的實現的 close()方法中會把 Connection 回收到連線池中。

參考並感謝

  • https://seata.io/zh-cn/docs/dev/mode/xa-mode.html
  • Mybatis 3原始碼深度解析

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024923/viewspace-2938231/,如需轉載,請註明出處,否則將追究法律責任。

相關文章