Java方法作为嵌套方法调用的函数中的参数(UPDATE)

时间:2011-06-14 13:18:42

标签: java sql jdbc clojure

我有一个通过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")))

问题似乎是需要申请,但不能用于。形式。

2 个答案:

答案 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)))))))