* Fix `with-transaction` spec and how it invokes `transact` when no options map is provided. * Adds type hint to `with-transaction`. * Document manual rollback of transactions.
This commit is contained in:
parent
85eba1bb2f
commit
1cec0a2643
7 changed files with 51 additions and 7 deletions
|
|
@ -6,6 +6,8 @@ Only accretive/fixative changes will be made from now on.
|
|||
|
||||
The following changes have been committed to the **master** branch since the 1.0.0 release:
|
||||
|
||||
* Fix #37 by adjusting the spec for `with-transaction` to "require less" of the `:binding` vector.
|
||||
* Fix #36 by adding type hint in `with-transaction` macro.
|
||||
* Fix #35 by explaining the database-specific options needed to ensure `insert-multi!` performs a single, batched operation.
|
||||
|
||||
## Stable Builds
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ The latest versions on Clojars and on cljdoc:
|
|||
|
||||
[](https://clojars.org/seancorfield/next.jdbc) [](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT)
|
||||
|
||||
This documentation is for the 1.0.0 release -- [see the CHANGELOG](CHANGELOG.md).
|
||||
This documentation is for **master**, beyond the 1.0.0 release -- [see the CHANGELOG](CHANGELOG.md).
|
||||
|
||||
* [Getting Started](/doc/getting-started.md)
|
||||
* [Migrating from `clojure.java.jdbc`](/doc/migration-from-clojure-java-jdbc.md)
|
||||
|
|
|
|||
|
|
@ -2,17 +2,26 @@
|
|||
|
||||
The `transact` function and `with-transaction` macro were briefly mentioned in the [Getting Started](/doc/getting-started.md) section but we'll go into more detail here.
|
||||
|
||||
Although `(transact transactable thunk)` is available, it is expected that you will mostly use `(with-transaction [tx transactable] body...)` when you want to execute multiple SQL operations in the context of a single transaction so that is what this section focuses on.
|
||||
Although `(transact transactable f)` is available, it is expected that you will mostly use `(with-transaction [tx transactable] body...)` when you want to execute multiple SQL operations in the context of a single transaction so that is what this section focuses on.
|
||||
|
||||
## Connection-level Control
|
||||
|
||||
By default, all connections that `next.jdbc` creates are automatically committable, i.e., as each operation is performed, the effect is committed to the database directly before the next operation is performed. Any exceptions only cause the current operation to be aborted -- any prior operations have already been committed.
|
||||
|
||||
It is possible to tell `next.jdbc` to create connections that do not automatically commit operations: pass `{:auto-commit false}` as part of the options map to anything that creates a connection (including `get-connection` itself). You can then decide when to commit or rollback by calling `.commit` or `.rollback` on the connection object itself. You can also create save points (`(.setSavePoint con)`, `(.setSavePoint con name)`) and rollback to them (`(.rollback con save-point)`). You can also change the auto-commit state of an open connection at any time (`(.setAutoCommit con on-off)`).
|
||||
|
||||
## Automatic Commit & Rollback
|
||||
|
||||
`next.jdbc`'s transaction handling provides a convenient baseline for either committing a group of operations if they all succeed or rolling them all back if any of them fails, by throwing an exception. You can either do this on an existing connection -- and `next.jdbc` will try to restore the state of the connection after the transaction completes -- or by providing a datasource and letting `with-transaction` create and manage its own connection:
|
||||
|
||||
```clojure
|
||||
(jdbc/with-transaction [tx my-datasource]
|
||||
(jdbc/execute! tx ...)
|
||||
(jdbc/execute! tx ...)) ; will commit, unless exception thrown
|
||||
|
||||
(jdbc/with-transaction [tx my-datasource]
|
||||
(jdbc/execute! tx ...)
|
||||
(when ... (throw ...)) ; will rollback
|
||||
(jdbc/execute! tx ...))
|
||||
```
|
||||
|
||||
|
|
@ -24,4 +33,18 @@ You can also provide an options map as the third element of the binding vector (
|
|||
|
||||
The latter can be particularly useful in tests, to run a series of SQL operations during a test and then roll them all back at the end.
|
||||
|
||||
## Manual Rollback Inside a Transaction
|
||||
|
||||
Instead of throwing an exception (which will propagate through `with-transaction` and therefore provide no result), you can also explicitly rollback if you want to return a result in that case:
|
||||
|
||||
```clojure
|
||||
(jdbc/with-transaction [tx my-datasource]
|
||||
(let [result (jdbc/execute! tx ...)]
|
||||
(if ...
|
||||
(do
|
||||
(.rollback tx)
|
||||
result)
|
||||
(jdbc/execute! tx ...))))
|
||||
```
|
||||
|
||||
[<: Prepared Statements](/doc/prepared-statements.md) | [All The Options :>](/doc/all-the-options.md)
|
||||
|
|
|
|||
|
|
@ -203,4 +203,5 @@
|
|||
* `:read-only` -- `true` / `false`,
|
||||
* `:rollback-only` -- `true` / `false`."
|
||||
[[sym transactable opts] & body]
|
||||
`(transact ~transactable (^{:once true} fn* [~sym] ~@body) ~opts))
|
||||
(let [con (vary-meta sym assoc :tag 'java.sql.Connection)]
|
||||
`(transact ~transactable (^{:once true} fn* [~con] ~@body) (or ~opts {}))))
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@
|
|||
:args (s/cat :binding (s/and vector?
|
||||
(s/cat :sym simple-symbol?
|
||||
:transactable ::transactable
|
||||
:opts ::opts-map))
|
||||
:opts (s/? ::opts-map)))
|
||||
:body (s/* any?)))
|
||||
|
||||
(s/fdef sql/insert!
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
(ns next.jdbc.transaction
|
||||
"SQL Transaction logic."
|
||||
(:require [next.jdbc.protocols :as p])
|
||||
(:import (java.sql Connection
|
||||
SQLException)))
|
||||
(:import (java.sql Connection)))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
|
|
|
|||
|
|
@ -110,13 +110,32 @@ VALUES ('Pear', 'green', 49, 47)
|
|||
"]))
|
||||
{:rollback-only true})))
|
||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||
(testing "with-transaction"
|
||||
(testing "with-transaction rollback-only"
|
||||
(is (= [{:next.jdbc/update-count 1}]
|
||||
(jdbc/with-transaction [t (ds) {:rollback-only true}]
|
||||
(jdbc/execute! t ["
|
||||
INSERT INTO fruit (name, appearance, cost, grade)
|
||||
VALUES ('Pear', 'green', 49, 47)
|
||||
"]))))
|
||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||
(testing "with-transaction exception"
|
||||
(is (thrown? Throwable
|
||||
(jdbc/with-transaction [t (ds)]
|
||||
(jdbc/execute! t ["
|
||||
INSERT INTO fruit (name, appearance, cost, grade)
|
||||
VALUES ('Pear', 'green', 49, 47)
|
||||
"])
|
||||
(throw (ex-info "abort" {})))))
|
||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"])))))
|
||||
(testing "with-transaction call rollback"
|
||||
(is (= [{:next.jdbc/update-count 1}]
|
||||
(jdbc/with-transaction [t (ds)]
|
||||
(let [result (jdbc/execute! t ["
|
||||
INSERT INTO fruit (name, appearance, cost, grade)
|
||||
VALUES ('Pear', 'green', 49, 47)
|
||||
"])]
|
||||
(.rollback t)
|
||||
result))))
|
||||
(is (= 4 (count (jdbc/execute! (ds) ["select * from fruit"]))))))
|
||||
|
||||
(deftest plan-misuse
|
||||
|
|
|
|||
Loading…
Reference in a new issue