Java-метод как параметр в функции с вызовами вложенных методов (UPDATE) - PullRequest
2 голосов
/ 14 июня 2011

У меня есть функция для получения метаданных JDBC через clojure-sql

(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, поскольку она одинакова для всех вызовов методов метаданных.

Я пытался переписать часть в resultset-seq с помощью нотаций doto, .., -> и (., Но не смог заставить ни одну из них работать.

Что я пропустил?

UPDATE: Следующее решение работает, но требуется взлом. Я не могу поверить, что необходимо отражение в 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 необходимо, но нельзя использовать на. форма.

Ответы [ 2 ]

2 голосов
/ 22 сентября 2011

Поскольку создание get-sql-метаданных для функции приведет к тому, что clojure оценит аргументы перед применением функции, невозможно передать ей «неизвестный» символ, такой как .getTables, который не может быть оценен для функции.

В макросах с другой стороны, аргументы оценки не оцениваются до применения макроса. Таким образом, создание макроса функции позволит вам сделать это.

как:

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

И, конечно, существует возможность преобразования вызова метода в функцию clojure с помощью макроса memfn, но я не думаю, что это то, что вам нужно

0 голосов
/ 14 июня 2011

Проблема в том, что вы пытаетесь вызвать .getTables для объекта "nil". Когда вы удалили (.getMetaData (sql / connection)), вы удалили получателя сообщения .getTables. Вам нужен объект MetaData, чтобы вы могли вызвать для него метод .getTables. Вы могли бы обернуть это в let:

(let [metadata (.getMetaData (sql/connection))]
  (get-sql-metadata db #(.getTables metatdata nil nil "%" (into-array '("TABLE")))))

UPDATE:

Как насчет этого:

(get-sql-metadata db (fn [meta] (.getTables meta nil nil "%" (into-array '("TABLE")))))

и затем метаданные get-table становятся:

(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)))))))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...