Add and document as-maps-adapter

This commit is contained in:
Sean Corfield 2019-08-21 14:19:32 -07:00
parent 0049b007e5
commit fed305dd2b
3 changed files with 35 additions and 0 deletions

View file

@ -8,6 +8,7 @@ The following changes have been committed to the **master** branch since the 1.0
* Fix #54 by improving documentation around data type conversions (and the `ReadableColumn` and `SettableParameter` protocols).
* Fix #52 by replacing `clojure.string/lower-case` with a US-locale function to avoid breakage in locales such as Turkish.
* Add `next.jdbc.result-set/as-maps-adapter` to provide a way to override the default result set reading behavior of using `.getObject`.
* Update `org.clojure/test.check` to `"0.10.0"`.
## Stable Builds

View file

@ -69,6 +69,8 @@ This namespace contains variants of the six `as-maps`-style builders above that
As mentioned above, when `with-column` is called, the expectation is that the row builder will call `.getObject` on the current state of the `ResultSet` object with the column index and will then call `read-column-by-index`, passing the column value, the `ResultSetMetaData`, and the column index. That function is part of the `ReadableColumn` protocol that you can extend to handle conversion of arbitrary database-specific types to Clojure values.
If you need more control over how values are read from the `ResultSet` object, you can use `next.jdbc.result-set/as-maps-adapter` which takes an existing builder function and a column reading function and returns a new builder function that calls your column reading function (with the `ResultSet` object, the `ResultSetMetaData` object, and the column index) instead of calling `.getObject` directly.
In addition, inside `plan`, as each value is looked up by name in the current state of the `ResultSet` object, the `read-column-by-label` function is called, again passing the column value and the column label (the name used in the SQL to identify that column). This function is also part of the `ReadableColumn` protocol.
The default implementation of this protocol is for these two functions to return `nil` as `nil`, a `Boolean` value as a canonical `true` or `false` value (unfortunately, JDBC drivers cannot be relied on to return unique values here!), and for all other objects to be returned as-is.

View file

@ -200,6 +200,38 @@
[rs opts]
(as-unqualified-modified-maps rs (assoc opts :label-fn lower-case)))
(defn as-maps-adapter
"Given a builder function (e.g., `as-maps`) and a column reading function,
return a new builder function that uses the column reading function
instead of `.getObject` so you can override the default behavior.
The default column-reader behavior would be equivalent to:
(defn default-column-reader
[^ResultSet rs ^ResultSetMetaData rsmeta ^Integer i]
(.getObject rs i))
Your column-reader can use the result set metadata to determine whether
to call `.getObject` or some other method to read the column's value."
[builder-fn column-reader]
(fn [rs opts]
(let [mrsb (builder-fn rs opts)]
(reify
RowBuilder
(->row [this] (->row mrsb))
(column-count [this] (column-count mrsb))
(with-column [this row i]
(assoc! row
(nth (:cols mrsb) (dec i))
(read-column-by-index (column-reader rs (:rsmeta mrsb) i)
(:rsmeta mrsb)
i)))
(row! [this row] (row! mrsb row))
ResultSetBuilder
(->rs [this] (->rs mrsb))
(with-row [this mrs row] (with-row mrsb mrs row))
(rs! [this mrs] (rs! mrsb mrs))))))
(defrecord ArrayResultSetBuilder [^ResultSet rs rsmeta cols]
RowBuilder
(->row [this] (transient []))