2010-11-12 2 views
0

У меня есть простая программа Clojure, которая запрашивает базу данных Oracle и выводит результаты в формате с разделителями-запятыми. Эта программа работает нормально, как обычно, через Clojure.main. Я хочу, чтобы скомпилировать эту программу и следовал инструкции из здесь:Программа Clojure не работает после компиляции

http://java.ociweb.com/mark/clojure/article.html#Compiling

Я скомпилировал и пробежал простую программу, используя этот метод, описанный в порядке. Поэтому я не думаю, что это связано с этим методом компиляции. Эта программа компилируется нормально, но когда я пытаюсь запустить скомпилированный класс возвращает эту ошибку:

Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: java.sql.SQLException: Exhausted Resultset at clojure.lang.LazySeq.sval(LazySeq.java:47) at clojure.lang.LazySeq.seq(LazySeq.java:56) at clojure.lang.RT.seq(RT.java:440) at clojure.core$seq__4245.invoke(core.clj:105) at bill.myquery$dump_db_csv__24.invoke(myquery.clj:27) at bill.myquery$main_49.invoke(myquery.clj:41) at clojure.lang.AFn.applyToHelper(AFn.java:171) at clojure.lang.AFn.applyTo(AFn.java:164) at bill.myquery.main(Unknown Source) Caused by: java.lang.RuntimeException: java.sql.SQLException: Exhausted Resultset at clojure.lang.LazySeq.sval(LazySeq.java:47) at clojure.lang.LazySeq.seq(LazySeq.java:56) at clojure.lang.Cons.next(Cons.java:37) at clojure.lang.RT.boundedLength(RT.java:1128) at clojure.lang.RestFn.applyTo(RestFn.java:135) at clojure.core$apply__4370.invoke(core.clj:438) at clojure.core$resultset_seq_6276$thisfn_6290$fn__6291.invoke(core.clj:3842) at clojure.lang.LazySeq.sval(LazySeq.java:42) ... 8 more Caused by: java.sql.SQLException: Exhausted Resultset at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112) at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146) at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208) at oracle.jdbc.driver.OracleResultSetImpl.getObject(OracleResultSetImpl.java:900) at clojure.core$resultset_seq__6276$row_values_6284$fn_6286.invoke(core.clj:3839) at clojure.core$map_5053$fn_5055.invoke(core.clj:1760) at clojure.lang.LazySeq.sval(LazySeq.java:42)

Вот оно:

(ns bill.myquery (:gen-class)) 

;; Import java.sql classes 
(import '(java.sql DriverManager Connection PreparedStatement ResultSet)) 

;; load jdbc/odbc driver 
(. Class forName "oracle.jdbc.driver.OracleDriver") 

(def dbname "myOracleServer:1522:myOracleDatabase") 
(def service_account "account") 
(def service_password "password") 

(def conn (. DriverManager (getConnection (str "jdbc:oracle:thin:@" dbname) service_account service_password))) 

(def sql " 
     SELECT name, address, phone 
     FROM addresss_book 
     ORDER BY name") 

;; Function to dump data with comma delimited fields 
(defn dump-db-csv [db] 
    (doseq [rec db] ;; for all rows 
    (doseq [[key value] rec] ;; for all fields 
     (if (= key :phone) ;; if last field 
       (print (.trim (str value))) ;; don't print comma 
       (print (format "%s%s" (.trim (str value)) ",")))) 
    (println))) 

;; Execute query and get recordset 
(def rs (.. conn (prepareStatement sql) (executeQuery))) 

;; convert recordset to sequence 
(def rset (resultset-seq rs)) 

;; Main call the function to print rows 
(defn -main (dump-db-csv rset)) 

;; close the recordset 
(. rs (close)) 

ответ

3

Не используйте def - все, от выполняется верхний уровень во время загрузки класса (или даже во время компиляции), поэтому вы закрываете набор результатов (rs) перед запуском -main. Всегда открывайте и закрывайте соединение в одной функции - это предотвратит множество ошибок.

Также рассмотрите возможность использования clojure.contrib.sql. Приятные примеры можно найти по адресу: wikibooks.

Для обоих методов не забудьте использовать doall для любых возможных ленивых последовательностей.

+0

Спасибо, я пропустил эту очевидную ошибку закрытия результирующего набора перед запуском -main. Я никогда раньше не компилировал Clojure и не замечал этого. Я не уверен, что вы имеете в виду, не используйте def и почему. Вы можете объяснить? Я использовал clojure.contrib.sql раньше, но эта программа настолько проста, что я действительно не ощущал ее. Еще раз спасибо. – Bill

+0

Во-первых, вы используете императивный стиль, а Clojure плохо подходит для него. Например, вы не можете использовать [переходные процессы] (http://clojure.org/transients) с императивными циклами, такими как «доза». А также с императивным стилем вы теряете всю силу ленивых последовательностей, поскольку они должны быть независимы от времени исполнения. См. [This] (http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-20.html#%_sec_3.1) для более полного примера. Во-вторых, используя 'def' вы вводите глобальные переменные, и это источник огромного количества ошибок. Поместите весь свой код в одну функцию 'main' и просто вызовите его в конце файла. – ffriend

+0

Кроме того, для упрощения компиляции рассмотрим использование [Leiningen] (https://github.com/technomancy/leiningen/). Это действительно простой в использовании инструмент, который уменьшает вашу работу по компиляции до нескольких строк кода. – ffriend

0

Я принял совет Андрея и использовал clojure.contrib.sql. Это делает его еще более простым и менее подверженным ошибкам. Я все еще не хотел отказываться от def для глобальных переменных db и sql.

Спасибо Андрей

(ns my.query (:use [clojure.contrib.sql])) 

(def *db* {:classname "oracle.jdbc.driver.OracleDriver" ; must be in classpath 
      :subprotocol "oracle:thin" 
      :subname "myserver:1521:mydatabase" 
      ; Any additional keys are passed to the driver 
      ; as driver-specific properties. 
      :user "account" 
      :password "password" }) 

(def *sql* "select * from phonebook") 

(defn dump-db-csv [] 
    (with-connection *db* 
    (transaction 
     (with-query-results rs [*sql*] 
      (doseq [row rs] ;; for all rows 
      (doseq [[key value] row] ;; for all fields 
       (if (= key :phone) ;; if last field 
        (print (.trim (str value))) ;; do not print comma 
        (print (format "%s%s" (.trim (str value)) ",")))) 
      (println)))))) 

;; Main - call the function to print rows 
(dump-db-csv) 
Смежные вопросы