Support clojure.java.jdbc/Connectable created by clojure.java.jdbc/with-db-transaction
needs analysis
**Describe the bug**
next-jdbc appears to not work well with the `clojure.java.jdbc/Connectable` created by `clojure.java.jdbc/with-db-transaction`. In particular I see `next.jdbc/execute-batch!` ignoring transactions and operating outside of them.
**To Reproduce**
Please see https://github.com/devurandom/next-jdbc-issue301 for a reproduction example.
It takes the following steps:
1. Prepare a PostgreSQL database with `CREATE TABLE table_a (id SERIAL PRIMARY KEY)`, `CREATE TABLE table_b (fkey INTEGER REFERENCES table_a (id))`
2. Create `{:datasource ...}` where `...` is a `javax.sql.DataSource`.
3. Within `clojure.java.jdbc/with-db-transaction [conn {:datasource ...}]` execute the following steps
4. Using `(:connection conn)` execute `INSERT INTO table_a (...) VALUES (...)` inside the transaction and "remember" the ID of the row inserted
5. Using `next.jdbc/execute-batch! conn ...` try to `INSERT INTO table_b (fkey) VALUES (ID remembered in step 4)`
6. Observe SQL error: The ID you inserted in step 4 supposedly does not exist.
The first step happens in `compose/docker-entrypoint-initdb.d/99-local.sh`, all others in `issue301-test/issue301-test`. `issue301-test/issue301-workaround-test` shows the workaround using `(:connection conn)`.
**Expected behavior**
next-jdbc notices that `conn` contains `:connection` and prefers that over `:datasource`.
**Environment**
- OS: Linux
- Java Version: Temurin 11
- Clojure Version: 1.12.0
- Database: PostgreSQL 14.17
- Driver Library Version: `org.postgresql/postgresql {:mvn/version "42.7.5"}`
**Additional context**
Relevant effect of `clojure.java.jdbc/with-db-transaction`, via `clojure.java.jdbc/db-transaction*`:
```clj
[...]
(with-open [con (get-connection db (dissoc opts :read-only?))]
(db-transaction* (add-connection db con) func opts))
[...]
```
`add-connection` is defined via extension of `clojure.java.jdbc/Connectable` in `clojure.java.jdbc`:
```clj
(extend-protocol Connectable
[...]
clojure.lang.Associative
(add-connection [m connection] (assoc m :connection connection))
(get-level [m] (or (:level m) 0))
[...])
```
Given a Clojure map of the shape `{:datasource ...}` this produces a Clojure map with the shape `{:datasource ... :connection ...}`.
If I read it correctly, next-jdbc uses this via `next.jdbc.protocols/Connectable` as extended in `next.jdbc.connection`:
```clj
(extend-protocol p/Connectable
[...]
Object
(get-connection [this opts] (p/get-connection (p/get-datasource this) opts)))
```
`next.jdbc.protocols/get-datasource` is defined as extension of `next.jdbc.protocols/Sourceable` in `next.jdbc.connection`:
```clj
(extend-protocol p/Sourceable
clojure.lang.Associative
(get-datasource [this]
;; #207 c.j.j compatibility:
(if-let [datasource (:datasource this)]
datasource
(url+etc->datasource
(if-let [uri (:connection-uri this)]
(string->url+etc uri)
(spec->url+etc this)))))
[...])
```
I.e. if the map passed has a `:datasource` key, it will be used and the `:connection` key will be ignored.
----
To fix / workaround this, I naively tried:
```clj
(extend-protocol p/Connectable
clojure.lang.Associative
(get-connection [this opts]
(or (:connection this)
(p/get-connection (p/get-datasource this) opts))))
```
But that resulted in:
```
clojure.java.jdbc/db-transaction* jdbc.clj: 789
clojure.java.jdbc/db-transaction* jdbc.clj: 852
clojure.java.jdbc/db-transaction* jdbc.clj: 818
clojure.java.jdbc/db-transaction*/fn jdbc.clj: 822
com.mchange.v2.c3p0.impl.NewProxyConnection.rollback NewProxyConnection.java: 1033
java.lang.NullPointerException:
java.sql.SQLException: You can't operate on a closed Connection!!!
errorCode: 0
clojure.lang.ExceptionInfo: Rollback failed handling "You can't operate on a closed Connection!!!"
```
It appears that next-jdbc functions badly interact with `clojure.java.jdbc/with-db-transaction` in general.
However, wrapping `conn` as `(some conn [:connection :datasource])` before passing it to `next.jdbc/execute-batch!` worked well.
See-also: https://github.com/seancorfield/next-jdbc/issues/207
关闭于 2025-05-12 6 条评论