问题描述
|
我有一个通过clojure-sql获取JDBC元数据的功能
(ns relink
(:require
[clojure.contrib.sql :as sql]
[clojure.string :as str]
))
(let [db-host \"localhost\"
db-port 1433
db-name \"databasename\"]
(def db {:classname \"com.microsoft.sqlserver.jdbc.sqlServerDriver\"
:subprotocol \"sqlserver\"
:subname (str \"//\" db-host \":\" db-port)
:databasename db-name
:user \"user\"
:password \"password\"}))
(defn get-table-Metadata
\"Take database spec,return all table names from the database Metadata\"
[db]
(sql/with-connection db
(doall
(resultset-seq
(.getTables
(.getMetaData (sql/connection))
nil nil \"%\" (into-array \'(\"TABLE\")))))))
(get-table-Metadata db)
现在,我想通过将该java方法调用包装在clojure函数中作为参数,将该函数扩展为使用其他sql元数据,如下所示:
(get-sql-Metadata db .getTables nil nil \"%\" (into-array \'(\"TABLE\")))
如果不将(.getMetaData(sql / connection))放入函数参数中,似乎也无法完成此操作。
(get-sql-Metadata db #(.getTables (.getMetaData (sql/connection)) nil nil \"%\" (into-array \'(\"TABLE\"))))
但是,我想在get-sql-Metadata函数中对此进行抽象,因为对于所有元数据方法调用而言都是相同的。
我已经尝试用doto,..,->和(。表示法重写resultset-seq中的部分,但它们都不起作用。
我错过了什么 ?
更新:
以下解决方案有效,但是需要一些技巧。我无法相信应该在str-invoke中进行反思,所以我不会将其作为答案。
(defn str-invoke [instance method-str & args]
(clojure.lang.Reflector/invokeInstanceMethod
instance
method-str
(to-array args)))
(defn get-sql-Metadata
\"Take database spec,Metadata method (as string) and method parameters\"
[db method & args]
(sql/with-connection db
(doall
(resultset-seq
(apply str-invoke
(.getMetaData (sql/connection))
method args)))))
(get-sql-Metadata db \"getTables\" nil nil \"%\" (into-array \'(\"TABLE\")))
问题似乎是需要应用,但不能在上使用。形成。
解决方法
由于将get-sql-metadata设置为函数将导致clojure在应用函数之前先评估参数,因此无法将无法评估的\“ unknown \”符号(如.getTables)传递给该函数。
另一方面,在宏中,在应用宏之前不评估参数。
因此,将函数设置为宏将允许您执行此操作。
喜欢:
(defmacro get-sql-metadata [db method & args]
`(with-connection
~db
(doall
(resultset-seq
(~method
(.getMetaData (connection))
~@args)))))
当然,也可以通过使用宏memfn将方法调用转换为clojure函数,但是我不认为这就是您想要的
, 问题是您正在尝试在对象“ nil”上调用.getTables。删除(.getMetaData(sql / connection))时,您也删除了.getTables消息的接收者。您需要MetaData对象,以便可以在其上调用.getTables方法。您可以将其包装成let:
(let [metadata (.getMetaData (sql/connection))]
(get-sql-metadata db #(.getTables metatdata nil nil \"%\" (into-array \'(\"TABLE\")))))
更新:
这个怎么样:
(get-sql-metadata db (fn [meta] (.getTables meta nil nil \"%\" (into-array \'(\"TABLE\")))))
然后get-table-metadata变成:
(defn get-table-metadata
\"Take database spec,return all table names from the database metadata\"
[db metafunction]
(sql/with-connection db
(doall
(resultset-seq
(metafunction (.getMetaData (sql/connection)))))))