【问题标题】:cx_Oracle-like package for ClojureClojure 的类 cx_Oracle 包
【发布时间】:2014-03-14 04:04:11
【问题描述】:

我来自一个非常繁重的 Python->Oracle 开发环境,并且一直在玩 Clojure。我喜欢 cx_Oracle 在 Python 端为我提供的对数据库的轻松访问,我想知道 Clojure 是否有类似的东西。

具体来说,我正在寻找能够让我轻松访问数据库连接的东西,也就是 cx_Oracle 的“用户名/密码@tns_name”格式。

到目前为止,我想出的最好的是:

(defn get-datasource [user password server service]
    {:datasource (clj-dbcp.core/make-datasource {:adapter :oracle
                                               :style :service-name
                                               :host server
                                               :service-name service
                                               :user user
                                               :password password})})

这需要服务器,但是我 95% 的用户不知道他们正在访问哪个服务器,只知道 tnsnames.ora 中的 tns 名称。

另外,我不明白我什么时候有数据库连接,什么时候断开连接。使用 cx_Oracle 我要么必须执行 with cx_Oracle.connect()...connection.close() 才能关闭连接。

有人可以指导我就连接而言数据源是如何工作的,以及在给定用户名、密码和 tns 别名的情况下连接到数据库的最简单方法吗?

谢谢!!

【问题讨论】:

    标签: python oracle jdbc clojure


    【解决方案1】:

    最好用 Clojure 最惯用的数据库库clojure.java.jdbc

    首先,由于 maven 存储库中没有 Oracle 驱动程序,我们需要下载最新的驱动程序并将其安装到我们的本地存储库中,使用 lein-localrepo 插件:

    lein localrepo install -r D:\Path\To\Repo\
                              D:\Path\To\ojdbc6.jar
                              oracle.jdbc/oracledriver "12.1.0.1"
    

    现在我们可以在 project.clj 中引用它,以及 clojure.java.jdbc。

    (defproject oracle-connect "0.1.0-SNAPSHOT"
      :dependencies [[org.clojure/java.jdbc "0.3.3"]
                     [oracle.jdbc/oracledriver "12.1.0.1"]])
    

    启动 REPL 后,我们可以通过默认主机/端口/SID 连接连接到数据库

    (ns oracle-connect
      (:require [clojure.java.jdbc :as jdbc]))
    
    (def db
      {:classname    "oracle.jdbc.OracleDriver"
       :subprotocol  "oracle:thin"
       :subname      "//@hostname:port:sid"
       :user         "username"
       :password     "password"}))
    
    (jdbc/query db ["select ? as one from dual" 1])
    

    db 只是一个基本映射,称为 db-spec。这不是真正的联系,但具有建立联系所需的所有信息。 Clojure.java.jdbc 在需要时生成一个,例如在(query db ..) 中。

    我们需要手动输入类名,因为 clojure.java.jdbc 在 Oracle 的子协议和类名之间没有默认映射。这可能是因为 Oracle JDBC 驱动程序同时具有瘦 JDBC 和 OCI JDBC 连接选项。

    要与 TNS 命名数据库建立连接,驱动程序需要 tnsnames.ora 文件的位置。这是通过设置一个名为 oracle.net.tns_admin 的系统属性来完成的。

    (System/setProperty "oracle.net.tns_admin"
                        "D:/oracle/product/12.1.0.1/db_1/NETWORK/ADMIN")
    

    设置完成后,我们所需要的 subname 就是数据库的 tnsname。

    (def db
      {:classname    "oracle.jdbc.OracleDriver"
       :subprotocol  "oracle:thin"
       :subname      "@tnsname"
       :user         "username"
       :password     "password"}))
    
    (jdbc/query db ["select ? as one from dual" 1])
    

    现在进入“连接如何工作”部分。如前所述,clojure.java.jdbc 在需要时创建连接,例如在查询函数中。

    如果您只想转换查询结果,您可以提供两个额外的可选命名参数::row-fn:result-set-fn。每一行都用 row-fn 转换,然后整个结果集用 result-set-fn 转换。

    这两者都是在连接的上下文中执行的,因此在执行所有这些操作之前,保证连接是打开的,除非这些函数返回惰性序列

    默认情况下 :result-set-fn 被定义为doall 保证实现所有结果,但如果你重新定义它一定要实现所有惰性结果。通常,每当您在使用范围之外的结果时遇到连接或结果集关闭异常时,问题就是您没有。

    连接只存在于query 函数的范围内。最后它是关闭的。这意味着每个查询都会产生一个连接。如果您想在一个连接中完成多个查询,可以将它们包装在 with-db-connection 中:

    (jdbc/with-db-connection [c db]
      (doall (map #(jdbc/query c ["select * from EMP where DEPTNO = ?" %])
                   (jdbc/query c ["select * from DEPT"] :row-fn :DEPTNO))))
    

    with-db-connection 绑定中,您将 db-spec 绑定到一个 var,并在绑定范围内的语句中使用该 var 而不是 db-spec。它创建一个连接并将其添加到 var。其他语句将使用该连接。这在根据其他查询的结果创建动态查询时特别方便。

    with-db-transaction 也是如此。它与with-db-connection 具有相同的语义,但是这里的作用域不仅保证使用相同的连接,而且通过将它们包装在事务块中来保证所有语句或不成功。 with-db-connectionwith-db-transaction 都是可嵌套的。

    还有更高级的选项,例如创建连接池,而不是 query 等。创建或重用单个连接,让它们从池中绘制连接。有关这些,请参阅clojure-doc.org documentation

    【讨论】:

    • 哇,我不得不说这是我在 stackoverflow 上读过的最好和最详细的答案之一。话虽如此,我需要一些时间来消化它并测试它,然后我会接受它作为答案。非常感谢!!
    • 所以,我无法连接。使用 :subname "@tnsalias" 我收到错误:java.sql.SQLRecoverableException: IO Error: Unknown host specified 如果我使用 :subname "tnsalias" 我收到错误:java.sql.SQLRecoverableException: IO Error: The Network适配器无法建立连接我可以使用 tnsalias tnsping db,所以我知道问题出在 clojure/java/etc 端。
    • 所有协议都是TCP
    • 问题是位置属性不是连接属性,而是系统属性。这些示例现在已修复并经过仔细检查。它在这里工作是因为我之前在 project.clj 中将其设置为 java 属性,但忘记将其删除。给您添麻烦了!
    猜你喜欢
    • 2012-06-30
    • 1970-01-01
    • 2017-10-03
    • 2016-01-18
    • 2012-08-11
    • 1970-01-01
    • 2010-11-01
    • 2019-12-21
    • 2014-08-24
    相关资源
    最近更新 更多