为您的驱动程序实现多方法
实现多方法让您可以利用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
这将打印出所有驱动程序命名空间和多态方法的列表。这包括许多像sql
和sql-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版本的文档。