【问题标题】:Java method as parameter in function with nested method calls (UPDATE)Java 方法作为具有嵌套方法调用的函数中的参数(更新)
【发布时间】:2011-06-14 13:18:41
【问题描述】:

我有一个通过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 元数据,方法是将 java 方法调用包装在 clojure 函数中作为参数,如下所示:

(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")))

问题似乎是需要 apply ,但不能在 .表格。

【问题讨论】:

    标签: java sql jdbc clojure


    【解决方案1】:

    由于将 get-sql-metadata 设为函数,因此 clojure 在应用函数之前会评估参数,因此不可能向其传递无法评估给函数的“未知”符号(如 .getTables)。

    另一方面,在宏中,评估参数在应用宏之前不会被评估。 因此,将函数设为宏将允许您执行此操作。

    喜欢:

    (defmacro get-sql-metadata [db method & args] 
      `(with-connection 
        ~db 
        (doall 
          (resultset-seq 
            (~method 
              (.getMetaData (connection)) 
              ~@args)))))
    

    当然还有可能通过使用宏 memfn 将方法调用转换为 clojure 函数,但我认为这不是你想要的

    【讨论】:

      【解决方案2】:

      问题是您试图在对象“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)))))))
      

      【讨论】:

      • 这样我仍然会在函数参数中传递 (.getMetaData (sql/connection)) ,只是形式不同。也许我应该改写这个问题,但我正在寻找一种方法如何在 get-sql-metadata 函数中(在 resultset-seq 中)将它抽象出来,只将 db 和 .getTables 方法及其参数放在函数参数中.
      • 解决方案有效,但函数参数几乎没有变得更清晰或更简洁。我自己找到了一个解决方案,我将在更新中发布,但我不敢相信它使用的反射步骤应该是必要的。
      猜你喜欢
      • 1970-01-01
      • 2017-09-18
      • 1970-01-01
      • 2016-06-09
      • 1970-01-01
      • 2021-03-11
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多