为你的驱动实现多方法

通过为你的特定数据库扩展现有方法,实现多方法可以让你利用 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 初始化驱动时(即在驱动首次建立数据库连接之前)使用 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 命名空间,以获取你可以实现的多方法的完整列表。阅读每个方法的 docstring 并决定是否需要实现它。大多数方法是可选的。

列出可用的驱动多方法

要快速查找所有驱动多方法的列表,你可以运行命令

clojure -M:run driver-methods

它将打印所有驱动命名空间和多方法的列表。这包括许多内容,如 sqlsql-jdbc 多方法,以及测试扩展多方法。

如果你也想查看方法的文档字符串,请运行

clojure -M:run driver-methods docs

父驱动

许多驱动共享实现细节,为同步方法等编写完整的实现会涉及大量重复代码。因此,**许多高级功能在共享的“父”驱动(例如最常见的父驱动 :sql-jdbc)中部分或完全实现**。“父”驱动类似于面向对象编程中的超类。

你可以通过在插件清单中列出父驱动来定义驱动父级。

:sql-jdbc 这样的父级旨在作为可以共享大部分实现的驱动的通用抽象“基类”;对于 :sql-jdbc 的情况,它适用于在底层使用 JDBC 驱动的基于 SQL 的驱动。:sql-jdbc 和其他父级提供了驱动程序四个主要功能所需的大多数方法的实现。实际上,: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 的文档。

这有帮助吗?

感谢您的反馈!
想要改进这些文档?提出更改。
© . This site is unofficial and not affiliated with Metabase, Inc.