address #256 by adding with-transaction+options

This commit is contained in:
Sean Corfield 2023-08-09 19:35:25 -07:00
parent 8ad1110fcb
commit 044de70b49
5 changed files with 76 additions and 4 deletions

View file

@ -2,6 +2,9 @@
Only accretive/fixative changes will be made from now on. Only accretive/fixative changes will be made from now on.
* 1.3.next in progress
* Address [#256](https://github.com/seancorfield/next-jdbc/issues/256) by adding `with-transaction+options`. Documentation TBD.
* 1.3.883 -- 2023-06-25 * 1.3.883 -- 2023-06-25
* Address [#254](https://github.com/seancorfield/next-jdbc/issues/254) by adding `next.jdbc/active-tx?` and adding more explanation to [**Transactions**](https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/doc/getting-started/transactions) about the conventions behind transactions and the limitations of thread-local tracking of active transactions in `next.jdbc`. * Address [#254](https://github.com/seancorfield/next-jdbc/issues/254) by adding `next.jdbc/active-tx?` and adding more explanation to [**Transactions**](https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/doc/getting-started/transactions) about the conventions behind transactions and the limitations of thread-local tracking of active transactions in `next.jdbc`.
* Address [#251](https://github.com/seancorfield/next-jdbc/issues/251) by updating `next.jdbc/with-logging` docstring. * Address [#251](https://github.com/seancorfield/next-jdbc/issues/251) by updating `next.jdbc/with-logging` docstring.

View file

@ -1,5 +1,7 @@
{:hooks {:hooks
{:analyze-call {:analyze-call
{next.jdbc/with-transaction {next.jdbc/with-transaction
hooks.com.github.seancorfield.next-jdbc/with-transaction}} hooks.com.github.seancorfield.next-jdbc/with-transaction
:lint-as {next.jdbc/on-connection clojure.core/with-open}} next.jdbc/with-transaction+options
hooks.com.github.seancorfield.next-jdbc/with-transaction+options}}
:lint-as {next.jdbc/on-connection clojure.core/with-open}}

View file

@ -16,3 +16,19 @@
opts opts
body))] body))]
{:node new-node}))) {:node new-node})))
(defn with-transaction+options
"Expands (with-transaction+options [tx expr opts] body)
to (let [tx expr] opts body) per clj-kondo examples."
[{:keys [:node]}]
(let [[binding-vec & body] (rest (:children node))
[sym val opts] (:children binding-vec)]
(when-not (and sym val)
(throw (ex-info "No sym and val provided" {})))
(let [new-node (api/list-node
(list*
(api/token-node 'let)
(api/vector-node [sym val])
opts
body))]
{:node new-node})))

View file

@ -389,6 +389,9 @@
Like `with-open`, if `with-transaction` creates a new `Connection` object, Like `with-open`, if `with-transaction` creates a new `Connection` object,
it will automatically close it for you. it will automatically close it for you.
If you are working with default options via `with-options`, you might want
to use `with-transaction+options` instead.
The options map supports: The options map supports:
* `:isolation` -- `:none`, `:read-committed`, `:read-uncommitted`, * `:isolation` -- `:none`, `:read-committed`, `:read-uncommitted`,
`:repeatable-read`, `:serializable`, `:repeatable-read`, `:serializable`,
@ -419,9 +422,45 @@
return plain Java objects, so if you call any of those on this wrapped return plain Java objects, so if you call any of those on this wrapped
object, you'll need to re-wrap the Java object `with-options` again. See object, you'll need to re-wrap the Java object `with-options` again. See
the Datasources, Connections & Transactions section of Getting Started for the Datasources, Connections & Transactions section of Getting Started for
more details, and some examples of use with these functions." more details, and some examples of use with these functions.
`with-transaction+options` exists to automatically rewrap a `Connection`
with the options from a `with-options` wrapper."
[connectable opts] [connectable opts]
(opts/->DefaultOptions connectable opts)) (let [c (:connectable connectable)
o (:options connectable)]
(if (and c o)
(opts/->DefaultOptions c (merge o opts))
(opts/->DefaultOptions connectable opts))))
(defmacro with-transaction+options
"Given a transactable object, assumed to be wrapped with options, gets a
connection, rewraps it with those options, and binds it to `sym`, then
executes the `body` in that context, committing any changes if the body
completes successfully, otherwise rolling back any changes made.
Like `with-open`, if `with-transaction+options` creates a new `Connection`
object, it will automatically close it for you.
Note: the bound `sym` will be a **wrapped** connectable and not a plain
Java object, so you cannot call JDBC methods directly on it like you can
with `with-transaction`.
The options map supports:
* `:isolation` -- `:none`, `:read-committed`, `:read-uncommitted`,
`:repeatable-read`, `:serializable`,
* `:read-only` -- `true` / `false` (`true` will make the `Connection` readonly),
* `:rollback-only` -- `true` / `false` (`true` will make the transaction
rollback, even if it would otherwise succeed)."
[[sym transactable opts] & body]
(let [con (vary-meta sym assoc :tag 'java.sql.Connection)]
`(let [tx# ~transactable]
(transact tx#
(^{:once true} fn*
[con#]
(let [~con (with-options con# (:options tx# {}))]
~@body))
~(or opts {})))))
(defn with-logging (defn with-logging
"Given a connectable/transactable object and a sql/params logging "Given a connectable/transactable object and a sql/params logging

View file

@ -65,6 +65,18 @@
ds-opts ds-opts
["select appearance as looks_like from fruit where id = ?" 1] ["select appearance as looks_like from fruit where id = ?" 1]
jdbc/snake-kebab-opts)))) jdbc/snake-kebab-opts))))
(let [ds' (jdbc/with-options ds-opts jdbc/snake-kebab-opts)]
(is (= "red" (:fruit/looks-like
(jdbc/execute-one!
ds'
["select appearance as looks_like from fruit where id = ?" 1])))))
(jdbc/with-transaction+options [ds' (jdbc/with-options ds-opts jdbc/snake-kebab-opts)]
(is (= (merge (default-options) jdbc/snake-kebab-opts)
(:options ds')))
(is (= "red" (:fruit/looks-like
(jdbc/execute-one!
ds'
["select appearance as looks_like from fruit where id = ?" 1])))))
(is (= "red" (:looks-like (is (= "red" (:looks-like
(jdbc/execute-one! (jdbc/execute-one!
ds-opts ds-opts