我有一个通过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)
现在我想扩展该函数以使用其他SQL元数据,方法是将clojure函数中的java方法调用作为参数包装,如下所示:
(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")))
问题似乎是需要申请,但不能用于。形式。
答案 0 :(得分:2)
在制作get-sql-metadata时,函数将导致clojure在应用函数之前评估参数,因此无法将其传递给像.getTables这样的“未知”符号,而这些符号无法对函数进行求值。
在另一方面的宏中,在应用宏之前不评估参数。 因此,将函数设置为宏将允许您执行此操作。
像:
(defmacro get-sql-metadata [db method & args]
`(with-connection
~db
(doall
(resultset-seq
(~method
(.getMetaData (connection))
~@args)))))
当然有可能通过使用宏memfn将方法调用转换为clojure函数,但我不认为这是你想要的
答案 1 :(得分:0)
问题是你试图在对象“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)))))))