Document next.jdbc.transaction/*nested-tx* fully

This commit is contained in:
Sean Corfield 2021-03-06 14:28:47 -08:00
parent 09382ea879
commit abc4d22302
3 changed files with 22 additions and 1 deletions

View file

@ -5,6 +5,7 @@ Only accretive/fixative changes will be made from now on.
## Stable Builds ## Stable Builds
* 1.1.next in progress * 1.1.next in progress
* Documented `next.jdbc.transaction/*nested-tx*` more thoroughly since that difference from `clojure.java.jdbc` has come up in conversation a few times recently.
* Fix #157 by copying `next.jdbc.prepare/execute-batch!` to `next.jdbc/execute-batch!` (to avoid a circular dependency that previously relied on requiring `next.jdbc.result-set` at runtime -- which was problematic for GraalVM-based native compilation); **`next.jdbc.prepare/execute-batch!` is deprecated:** it will continue to exist and work, but is no longer documented. In addition, `next.jdbc.prepare/execute-batch!` now relies on a private `volatile!` in order to reference `next.jdbc.result-set/datafiable-result-set` so that it is GraalVM-friendly. Note: code that requires `next.jdbc.prepare` and uses `execute-batch!` without also requiring something that causes `next.jdbc.result-set` to be loaded will no longer return generated keys from `execute-batch!` but that's an almost impossible path since nearly all code that uses `execute-batch!` will have called `next.jdbc/prepare` to get the `PreparedStatement` in the first place. * Fix #157 by copying `next.jdbc.prepare/execute-batch!` to `next.jdbc/execute-batch!` (to avoid a circular dependency that previously relied on requiring `next.jdbc.result-set` at runtime -- which was problematic for GraalVM-based native compilation); **`next.jdbc.prepare/execute-batch!` is deprecated:** it will continue to exist and work, but is no longer documented. In addition, `next.jdbc.prepare/execute-batch!` now relies on a private `volatile!` in order to reference `next.jdbc.result-set/datafiable-result-set` so that it is GraalVM-friendly. Note: code that requires `next.jdbc.prepare` and uses `execute-batch!` without also requiring something that causes `next.jdbc.result-set` to be loaded will no longer return generated keys from `execute-batch!` but that's an almost impossible path since nearly all code that uses `execute-batch!` will have called `next.jdbc/prepare` to get the `PreparedStatement` in the first place.
* 1.1.613 -- 2020-11-05 * 1.1.613 -- 2020-11-05

View file

@ -106,6 +106,7 @@ These are mostly drawn from [Issue #5](https://github.com/seancorfield/next-jdbc
* Keyword options no longer end in `?` -- for consistency (in `clojure.java.jdbc`, some flag options ended in `?` and some did not; also some options that ended in `?` accepted non-`Boolean` values, e.g., `:as-arrays?` and `:explain?`), * Keyword options no longer end in `?` -- for consistency (in `clojure.java.jdbc`, some flag options ended in `?` and some did not; also some options that ended in `?` accepted non-`Boolean` values, e.g., `:as-arrays?` and `:explain?`),
* `with-db-connection` has been replaced by just `with-open` containing a call to `get-connection`, * `with-db-connection` has been replaced by just `with-open` containing a call to `get-connection`,
* `with-transaction` can take a `:rollback-only` option, but there is no built-in way to change a transaction to rollback _dynamically_; either throw an exception (all transactions roll back on an exception) or call `.rollback` directly on the `java.sql.Connection` object (see [Manual Rollback Inside a Transactions](/doc/transactions.md#manual-rollback-inside-a-transaction) and the following section about save points), * `with-transaction` can take a `:rollback-only` option, but there is no built-in way to change a transaction to rollback _dynamically_; either throw an exception (all transactions roll back on an exception) or call `.rollback` directly on the `java.sql.Connection` object (see [Manual Rollback Inside a Transactions](/doc/transactions.md#manual-rollback-inside-a-transaction) and the following section about save points),
* `clojure.java.jdbc` implicitly allowed transactions to nest and just silently ignored the inner, nested transactions (so you only really had the top-level, outermost transaction); `next.jdbc` by default assumes you know what you are doing and so an inner (nested) transaction will commit or rollback the work done so far in outer transaction (and then when that outer transaction ends, the remaining work is rolled back or committed); `next.jdbc.transaction/*nested-tx*` is a dynamic var that can be bound to `:ignore` to get the same behavior as `clojure.java.jdbc`.
* The extension points for setting parameters and reading columns are now `SettableParameter` and `ReadableColumn` protocols. * The extension points for setting parameters and reading columns are now `SettableParameter` and `ReadableColumn` protocols.
[<: `datafy`, `nav`, and `:schema`](/doc/datafy-nav-and-schema.md) [<: `datafy`, `nav`, and `:schema`](/doc/datafy-nav-and-schema.md)

View file

@ -53,7 +53,7 @@ Instead of throwing an exception (which will propagate through `with-transaction
In general, transactions are per-connection and do not nest in JDBC. If you nest calls to `with-transaction` using a `DataSource` argument (or a db-spec) then you will get separate connections inside each invocation and the transactions will be independent, as permitted by the isolation level. In general, transactions are per-connection and do not nest in JDBC. If you nest calls to `with-transaction` using a `DataSource` argument (or a db-spec) then you will get separate connections inside each invocation and the transactions will be independent, as permitted by the isolation level.
If you nest such calls passing a `Connection` instead, the inner call will commit (or rollback) all operations on that connection up to that point -- including any performed in the outer call, prior to entering the inner call. The outer call will then commit (or rollback) any additional operations within its scope. This will be confusing at best and most likely buggy behavior! If you nest such calls passing a `Connection` instead, the inner call will commit (or rollback) all operations on that connection up to that point -- including any performed in the outer call, prior to entering the inner call. The outer call will then commit (or rollback) any additional operations within its scope. This will be confusing at best and most likely buggy behavior! See below for ways to exercise more control over this behavior.
If you want the ability to selectively roll back certain groups of operations inside a transaction, you can use named or unnamed save points: If you want the ability to selectively roll back certain groups of operations inside a transaction, you can use named or unnamed save points:
@ -76,4 +76,23 @@ If you want the ability to selectively roll back certain groups of operations in
;; (and ops B & C if they weren't rolled back above) ;; (and ops B & C if they weren't rolled back above)
``` ```
### Nesting Transactions
As noted above, transactions do not nest in JDBC and `next.jdbc`'s default behavior is to allow you
to overlap transactions (i.e., nested calls to `with-transaction`) and assume you know what you are
doing, although it would generally be buggy programming to do so.
By contrast, `clojure.java.jdbc` allowed the nested calls but simply _ignored_ the inner calls and
behaved as it you had only the outermost, top-level transaction. That allowed for buggy programming
too, in a different way, but could be convenient if you wanted to override any transaction behavior
in called code, as you might wish to do with a test fixture that set up and rolled back a
transaction at the top-level -- you would just silently lose the effects of any (nested)
transactions in the code under test.
`next.jdbc` provides a way to control the behavior via a public, dynamic Var:
* `next.jdbc.transaction/*nested-tx*` is initially set to `:allow` which allows nested calls but makes them overlap (as described above),
* `(binding [next.jdbc.transaction/*nested-tx* :ignore] ...)` provides the same behavior as `clojure.java.jdbc` where nested calls are essentially ignored and only the outermost transaction takes effect,
* `(binding [next.jdbc.transaction/*nested-tx* :prohibit] ...)` will cause any attempt to start a nested transaction to throw an exception instead; this could be a useful way to detect the potentially buggy behavior described above (for either `:allow` or `:ignore`).
[<: Prepared Statements](/doc/prepared-statements.md) | [All The Options :>](/doc/all-the-options.md) [<: Prepared Statements](/doc/prepared-statements.md) | [All The Options :>](/doc/all-the-options.md)