【问题标题】:Clojure Program Doesn't Work After CompilingClojure 程序在编译后不起作用
【发布时间】:2010-11-12 23:21:10
【问题描述】:

我有一个简单的 Clojure 程序,它查询 Oracle 数据库并以逗号分隔的格式打印出结果。该程序可以正常通过 Clojure.main 运行。我想编译这个程序并按照这里的说明进行操作:

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

我使用这个描述的方法编译并运行了另一个简单的程序。所以我认为这与这种编译方法没有任何关系。该程序编译得很好,但是当我尝试运行已编译的类时,它会返回此错误:

线程“main”中的异常 java.lang.RuntimeException: java.lang.RuntimeException: java.sql.SQLException:用尽 结果集 在 clojure.lang.LazySeq.sval(LazySeq.java:47) 在 clojure.lang.LazySeq.seq(LazySeq.java:56) 在 clojure.lang.RT.seq(RT.java:440) 在 clojure.core$seq__4245.invoke(core.clj:105) 在 bill.myquery$dump_db_csv__24.invoke(myquery.clj:27) 在 bill.myquery$main_49.invoke(myquery.clj:41) 在 clojure.lang.AFn.applyToHelper(AFn.java:171) 在 clojure.lang.AFn.applyTo(AFn.java:164) 在 bill.myquery.main(未知来源) 引起:java.lang.RuntimeException: java.sql.SQLException:用尽 结果集 在 clojure.lang.LazySeq.sval(LazySeq.java:47) 在 clojure.lang.LazySeq.seq(LazySeq.java:56) 在 clojure.lang.Cons.next(Cons.java:37) 在 clojure.lang.RT.boundedLength(RT.java:1128) 在 clojure.lang.RestFn.applyTo(RestFn.java:135) 在 clojure.core$apply__4370.invoke(core.clj:438) 在 clojure.core$resultset_seq_6276$thisfn_6290$fn__6291.invoke(core.clj:3842) 在 clojure.lang.LazySeq.sval(LazySeq.java:42) ... 8 更多原因:java.sql.SQLException: Exhausted 结果集 在 oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112) 在 oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146) 在 oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208) 在 oracle.jdbc.driver.OracleResultSetImpl.getObject(OracleResultSetImpl.java:900) 在 clojure.core$resultset_seq__6276$row_values_6284$fn_6286.invoke(core.clj:3839) 在 clojure.core$map_5053$fn_5055.invoke(core.clj:1760) 在 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))

【问题讨论】:

    标签: oracle clojure


    【解决方案1】:

    不要使用def - 顶层的所有内容都在类加载时(甚至在编译时)执行,因此在运行-main 之前关闭结果集(rs)。始终在单个函数中打开和关闭连接 - 它可以防止很多错误。

    还可以考虑使用clojure.contrib.sql。很好的例子可以在wikibooks找到。

    对于这两种方法,不要忘记将doall 用于任何可能的惰性序列。

    【讨论】:

    • 谢谢,我错过了在运行 -main 之前关闭结果集的明显错误。我以前从未编译过 Clojure 并忽略了这一点。我不确定您所说的不使用 def 是什么意思以及为什么。你可以解释吗?我之前使用过 clojure.contrib.sql,但是这个程序非常简单,我并没有真正感到需要。再次感谢。
    • 首先,您使用命令式样式,而 Clojure 非常适合它。例如,您不能将transientsdoseq 之类的命令式循环一起使用。并且使用命令式风格,你失去了惰性序列的所有力量,因为它们必须独立于执行时间。有关更全面的示例,请参阅this。其次,使用def 会引入全局变量,这会导致大量错误。将所有代码放入一个 main 函数中,然后在文件末尾调用它。
    • 另外,为了更容易编译考虑使用Leiningen。这是一款非常容易上手的工具,可将您的编译工作减少到几行代码。
    【解决方案2】:

    我接受了 Andrei 的建议并使用了 clojure.contrib.sql。它确实使这更简单,更不容易出错。我还是不想放弃 db 和 sql 全局变量的 def。

    谢谢安德烈

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

    【讨论】:

      猜你喜欢
      • 2022-09-28
      • 1970-01-01
      • 2018-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-28
      • 2019-09-19
      相关资源
      最近更新 更多