为您的驱动程序实现多方法

实现多方法让您可以利用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 使用 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的情况下,它被设计用于底层使用JDBC驱动的基于SQL的驱动程序。: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并构建驱动程序uberjars会相当讨厌,尤其是如果您需要重复执行以测试每个更改。幸运的是,您可以像运行单个大型项目中的命令一样运行命令

要启动REPL。

clojure -A:dev:drivers:drivers-dev

您需要重新构建驱动程序并将其安装到您的./plugins目录中,并在您进行更改时重启Metabase。

阅读关于其他Metabase版本的文档。

想要改进这些文档? 提出更改建议。