為您的驅動程式實作多方法

實作多方法可讓您運用 Metabase 現有的驅動程式碼,方法是擴充這些方法以適用於您的特定資料庫。

讓我們先將重點放在 Fox Pro ‘98 的主要驅動程式檔案 src/metabase/driver/foxpro98.clj。看看這段範例程式碼

;; Define a namespace for the driver
(ns com.mycompany.metabase.driver.foxpro98
  (:require [metabase.driver :as driver]))

;; Can you include a different method here as an example?
(defmethod driver/display-name :foxpro98 [_]
  "Visual FoxPro '98")

讓我們逐步瞭解每個程式碼區塊。

驅動程式命名空間

;; Define a namespace for the driver
(ns com.mycompany.metabase.driver.foxpro98
  (:require [metabase.driver :as driver]))

每個 Metabase 驅動程式都存在於自己的命名空間中

在此案例中,命名空間為 com.mycompany.metabase.driver.foxpro98。所有核心 Metabase 驅動程式都位於 metabase.driver.<name-goes-here> 命名空間中。最好使用符合 Java 套件命名慣例 的名稱。

許多驅動程式會進一步細分為其他命名空間

尤其是較大的驅動程式。一般而言,驅動程式會有一個 query-processor 命名空間 (例如,com.mycompany.metabase.driver.foxpro98.query-processor),其中包含將 MBQL 查詢 (使用 Metabase 圖形化查詢產生器建立的查詢) 轉換為原生查詢 (例如 SQL) 的邏輯。查詢處理器通常是驅動程式中最複雜的部分,因此將該邏輯分開有助於簡化工作流程。有些驅動程式也有個別的 sync 命名空間,其中包含 Metabase 的 資料庫同步 所使用的方法實作。

驅動程式初始化

所有驅動程式都可以包含額外的程式碼,以便在使用 metabase.driver/initialize! 初始化驅動程式時 (也就是在驅動程式第一次建立資料庫連線之前) 執行一次 (而且只執行一次)。(事實上,Metabase 使用 metabase.driver/initialize! 來延遲載入驅動程式。) 只有在少數情況下才應使用 metabase.driver/initialize,例如配置資源或設定某些系統屬性。

metabase.driver 多方法

metabase.driver 命名空間 定義了一系列 多方法,而驅動程式會為它們提供實作,如同我們的範例

(defmethod driver/display-name :foxpro98 [_]
  "Visual FoxPro '98")

上述 Metabase 驅動程式的四個主要功能都透過多方法實作。這些方法會依據驅動程式的關鍵字 :foxpro98 (在我們的案例中) 進行分派。事實上,這就是 Metabase 驅動程式的全部 – 一個關鍵字!沒有任何類別或物件可見 – 只有一個關鍵字。

您可以瀏覽 metabase.driver 命名空間,以取得您可以實作的完整多方法清單。閱讀每個方法的說明字串,並決定是否需要實作它。大多數方法都是選用的。

列出可用的驅動程式多方法

若要快速查詢所有驅動程式多方法的清單,您可以執行下列指令

clojure -M:run driver-methods

這會列印所有驅動程式命名空間和多方法的清單。這包括許多項目,例如 sqlsql-jdbc 多方法,以及測試擴充功能多方法。

如果您也想查看方法的說明字串,請執行

clojure -M:run driver-methods docs

父驅動程式

許多驅動程式共用實作細節,而且為同步方法等撰寫完整的實作會牽涉到許多重複的程式碼。因此,許多高階功能會在共用的「父」驅動程式中部分或完整實作,例如最常見的父驅動程式 :sql-jdbc。「父」驅動程式類似於物件導向程式設計中的超類別。

您可以透過在 外掛程式資訊清單 中列出父驅動程式來定義驅動程式父驅動程式。

:sql-jdbc 這類的父驅動程式旨在作為驅動程式的通用抽象「基底類別」,這些驅動程式可以共用其大部分的實作;在 :sql-jdbc 的案例中,它適用於以 SQL 為基礎且在底層使用 JDBC 驅動程式的驅動程式。:sql-jdbc 和其他父驅動程式為 Metabase 驅動程式的四個主要功能提供許多方法實作。事實上,:sql-jdbc 提供諸如 driver/execute-prepared-statement! 之類項目的實作,因此使用它作為父驅動程式的驅動程式不需要自行提供實作。不過,各種父驅動程式會定義自己的多方法來實作。

值得注意的父驅動程式

這些父驅動程式非常重要。

  • :sql-jdbc 可以作為具有 JDBC 驅動程式之以 SQL 為基礎的資料庫的父驅動程式使用。
    • :sql-jdbc 實作了四個主要功能中的大多數,但您必須實作在 metabase.driver.sql-jdbc.* 命名空間中找到的 sql-jdbc 多方法,以及 metabase.driver.sql.* 命名空間中的某些方法。
  • :sql 本身是 :sql-jdbc 的父驅動程式;它可以用於沒有 JDBC 驅動程式的以 SQL 為基礎的資料庫,例如 BigQuery。
    • :sql 實作了大量的驅動程式功能,但您必須實作在 metabase.driver.sql.* 命名空間中找到的某些方法才能使用它。
  • 有些驅動程式使用其他「具體」的驅動程式作為其父驅動程式 – 例如,:redshift 使用 :postgres 作為父驅動程式,僅提供方法實作來覆寫 postgres 驅動程式 (在需要時)。

呼叫父驅動程式實作

您可以使用 get-method 取得父驅動程式的方法實作

(defmethod driver/mbql->native :bigquery [driver query]
  ((get-method driver/mbql-native :sql) driver query))

這相當於在物件導向程式設計中呼叫 super.someMethod()

您必須將驅動程式引數以原樣傳遞至父實作,以便該方法呼叫的任何方法都使用正確的實作。以下是您應避免的兩種呼叫父驅動程式的方式

(defmethod driver/mbql->native :bigquery [_ query]
  ;; BAD! If :sql's implementation of mbql->native calls any other methods, it won't use the :bigquery implementation
  ((get-method driver/mbql->native :sql) :sql query))

也請避免

(defmethod driver/mbql->native :bigquery [_ query]
  ;; BAD! If someone else creates a driver using :bigquery as a parent, any methods called by :sql's implementation
  ;; of mbql->native will use :bigquery method implementations instead of custom ones for that driver
  ((get-method driver/mbql->native :sql) :bigquery query))

多個父驅動程式

眼尖的讀者可能已經注意到,BigQuery 被提及同時具有 :sql:google 作為父驅動程式。允許並支援這種多重繼承!您可以透過以下方式定義具有多個父驅動程式的驅動程式

(driver/register! :bigquery, :parent #{:sql :google})

在某些情況下,兩個父驅動程式都可能為某個方法提供實作;若要修正這種不明確性,只需為您的驅動程式提供實作,並如上所述將它們傳遞至慣用的父驅動程式實作即可。

對於以外掛程式形式出貨的驅動程式,您會在外掛程式資訊清單中註冊方法。

從 REPL 和 CIDER 中使用驅動程式

必須在本機安裝 metabase-core 並建置驅動程式 uberjar 會很麻煩,尤其是當您必須重複此動作來測試每個變更時。幸運的是,您可以執行指令,就像一切都是一個大型專案的一部分一樣

啟動 REPL。

clojure -A:dev:drivers:drivers-dev

您需要重建驅動程式並將其安裝在您的 ./plugins 目錄中,並在您進行變更時重新啟動 Metabase。

閱讀其他 Metabase 版本的文件。