with-connection 不仅因为 leonardoborges 提到的原因被认为是有害的,而且还使得使用连接池变得更加困难。将函数数据库访问与特定连接解耦使得模型更容易。强制查询使用相同的连接应该是例外,而不是规则。所以 clojure.core/java.jdbc 0.3.0 是专门为弃用 with-connection 而设计的。为了适应这种情况,需要更改整个 API。
现在每个数据库访问函数都将 db 规范作为参数。如果 db-spec 是一个连接池,该函数将在它的一个连接上执行,否则从 db-spec 建立一个隐式的新连接。因此,所有数据库访问函数都会在不使用连接池时产生一个连接。
这也意味着结果集不能再延迟返回。以前,处理惰性序列可能会在仍然在 with-connection 块内时被推迟。现在需要在函数执行过程中实现,或者关闭它的连接或者从池中返回一个新的连接用于下一个访问函数。
因此现在可以通过两个新的命名参数在函数本身的范围内完成处理::row-fn 和 :result-set-fn。第一个转换每一行,第二个转换行的集合。如果:result-set-fn 返回一个惰性序列,那么稍后使用它时会出现连接或结果集关闭异常。默认:result-set-fn 是doall。使用您自己的时,请确保已实现。
在一般情况下,访问和连接是分离的。现在例外:需要使用相同连接的函数。
其中最常见的是事务使用,它使用范围来指示事务的开始和结束。旧的transaction 只提供了这个范围。新的 with-db-transaction 函数采用新 var 和 dp-spec 的绑定。
这个 var 将绑定到池中的一个特定连接,或者当没有使用连接池时,一个新创建的连接。块内使用的所有 db 访问函数都应使用 var 而不是 db-spec 参数。
(def db {..})
(with-db-transaction
[c db]
(let [from 1111
to 2222
sum 10
saldo-from (query c ["select saldo from account where id=?" from]
:row-fn :saldo
:result-set-fn first)
saldo-to (query c ["select saldo from account where id=?" to]
:row-fn :saldo
:result-set-fn first)]
(update! c :account {:saldo (- saldo-from sum)} ["id=?" from])
(update! c :account {:saldo (+ saldo-to sum)} ["id=?" to])))
begin transaction 命令将在开始时发出。所有访问都将使用现在专门传递给函数的相同连接,而不是通过动态作用域魔法。当没有产生异常时,一个commit会在作用域的末尾给出。
当一个特定的连接,但不需要事务机制时,with-db-connection 函数具有相同的语义。因此,如果您想执行命令来设置会话设置,并对该连接进行一些查询,您可以执行以下操作:
(def db {..})
(with-db-connection [c db]
(execute! c ["alter session set NLS_SORT='ITALIAN'"])
(query c ["select * from person where name=?" "Mario"]
:row-fn (comp concat (juxt :name :surname))))
连接池通常具有特定的on-open 和on-close 命令,它们是其规范的一部分。使用来自该池的所有连接将设置相同的会话设置,甚至根本不需要with-db-connection。