Add as-arrays-adapter
This commit is contained in:
parent
57f146ee41
commit
9a76e4c25a
4 changed files with 53 additions and 5 deletions
|
|
@ -8,7 +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 #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.
|
* 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`.
|
* Add `next.jdbc.result-set/as-maps-adapter` and `next.jdbc.result-set/as-arrays-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"`.
|
* Update `org.clojure/test.check` to `"0.10.0"`.
|
||||||
|
|
||||||
## Stable Builds
|
## Stable Builds
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ 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.
|
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.
|
If you need more control over how values are read from the `ResultSet` object, you can use `next.jdbc.result-set/as-maps-adapter` (or `next.jdbc.result-set/as-arrays-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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,9 +201,9 @@
|
||||||
(as-unqualified-modified-maps rs (assoc opts :label-fn lower-case)))
|
(as-unqualified-modified-maps rs (assoc opts :label-fn lower-case)))
|
||||||
|
|
||||||
(defn as-maps-adapter
|
(defn as-maps-adapter
|
||||||
"Given a builder function (e.g., `as-maps`) and a column reading function,
|
"Given a map builder function (e.g., `as-lower-maps`) and a column reading
|
||||||
return a new builder function that uses the column reading function
|
function, return a new builder function that uses that column reading
|
||||||
instead of `.getObject` so you can override the default behavior.
|
function instead of `.getObject` so you can override the default behavior.
|
||||||
|
|
||||||
The default column-reader behavior would be equivalent to:
|
The default column-reader behavior would be equivalent to:
|
||||||
|
|
||||||
|
|
@ -303,6 +303,37 @@
|
||||||
[rs opts]
|
[rs opts]
|
||||||
(as-unqualified-modified-arrays rs (assoc opts :label-fn lower-case)))
|
(as-unqualified-modified-arrays rs (assoc opts :label-fn lower-case)))
|
||||||
|
|
||||||
|
(defn as-arrays-adapter
|
||||||
|
"Given an array builder function (e.g., `as-unqualified-arrays`) and a column
|
||||||
|
reading function, return a new builder function that uses that 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 [arsb (builder-fn rs opts)]
|
||||||
|
(reify
|
||||||
|
RowBuilder
|
||||||
|
(->row [this] (->row arsb))
|
||||||
|
(column-count [this] (column-count arsb))
|
||||||
|
(with-column [this row i]
|
||||||
|
(conj! row
|
||||||
|
(read-column-by-index (column-reader rs (:rsmeta arsb) i)
|
||||||
|
(:rsmeta arsb)
|
||||||
|
i)))
|
||||||
|
(row! [this row] (row! arsb row))
|
||||||
|
ResultSetBuilder
|
||||||
|
(->rs [this] (->rs arsb))
|
||||||
|
(with-row [this mrs row] (with-row arsb mrs row))
|
||||||
|
(rs! [this mrs] (rs! arsb mrs))))))
|
||||||
|
|
||||||
(declare navize-row)
|
(declare navize-row)
|
||||||
|
|
||||||
(defprotocol DatafiableRow
|
(defprotocol DatafiableRow
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,23 @@
|
||||||
;; and all its corresponding values should be ints
|
;; and all its corresponding values should be ints
|
||||||
(is (every? int? (map first (rest rs))))
|
(is (every? int? (map first (rest rs))))
|
||||||
(is (every? string? (map second (rest rs)))))
|
(is (every? string? (map second (rest rs)))))
|
||||||
|
(let [rs (jdbc/execute! ; test again, with adapter and lower columns
|
||||||
|
(ds)
|
||||||
|
["select * from fruit order by id"]
|
||||||
|
{:builder-fn (rs/as-arrays-adapter
|
||||||
|
rs/as-lower-arrays
|
||||||
|
(fn [^ResultSet rs _ ^Integer i]
|
||||||
|
(.getObject rs i)))})]
|
||||||
|
(is (every? vector? rs))
|
||||||
|
(is (= 5 (count rs)))
|
||||||
|
(is (every? #(= 5 (count %)) rs))
|
||||||
|
;; columns come first
|
||||||
|
(is (every? qualified-keyword? (first rs)))
|
||||||
|
;; :fruit/id should be first column
|
||||||
|
(is (= :fruit/id (ffirst rs)))
|
||||||
|
;; and all its corresponding values should be ints
|
||||||
|
(is (every? int? (map first (rest rs))))
|
||||||
|
(is (every? string? (map second (rest rs)))))
|
||||||
(let [rs (jdbc/execute!
|
(let [rs (jdbc/execute!
|
||||||
(ds)
|
(ds)
|
||||||
["select * from fruit order by id"]
|
["select * from fruit order by id"]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue