为您的驱动程序实现多方法
实现多方法可以让您通过扩展现有驱动程序代码来为您的特定数据库利用 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)的逻辑。查询处理器通常是驱动程序中最复杂的部分,因此将该逻辑分开可以更轻松地处理。
驱动程序初始化
所有驱动程序都可以使用 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
这将打印出所有驱动程序命名空间和多方法的列表。这包括 sql 和 sql-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 并构建驱动程序 uberjars 会很麻烦,尤其是当您不得不重复进行以测试每次更改时。幸运的是,您可以运行命令,就好像所有内容都属于一个巨大的项目一样。
启动 REPL。
clojure -A:dev:drivers:drivers-dev
当您进行更改时,需要重建驱动程序并将其安装在 ./plugins 目录中,然后重新启动 Metabase。
阅读其他版本的 Metabase 的文档。