我来自一个非常繁重的Python-> Oracle开发环境,并且一直在玩Clojure。我喜欢cx_Oracle带给我的Python端数据库的易用性,并且想知道Clojure是否有类似的东西。
具体而言,我正在寻找的是让我轻松访问数据库连接,ala 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别名连接数据库的最简单方法向我提供指导吗?
谢谢!
答案 0 :(得分:3)
最好使用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驱动程序同时具有精简和OCI JDBC连接选项。
要与名为TNS的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 in语句。它创建一个连接并将其添加到var。其他语句将使用该连接。在基于其他查询的结果创建动态查询时,这尤其方便。
同样适用于with-db-transaction
。它具有与with-db-connection
相同的语义,但是这里的范围不仅保证使用相同的连接,而且通过将它们包装在事务块中,所有语句或者都不成功。 with-db-connection
和with-db-transaction
都可以嵌套。
还有更多高级选项,例如创建连接池而不是query
等。创建或重用单个连接,让它们从池中绘制连接。有关这些内容,请参阅clojure-doc.org documentation。