Add optional maps adapter
This commit is contained in:
parent
e856ca1919
commit
04237c89ea
3 changed files with 98 additions and 1 deletions
|
|
@ -8,6 +8,7 @@ The following changes have been committed to the **master** branch since the 1.0
|
|||
|
||||
* Address #68 by clarifying that builder functions do not affect the "fake result set" containing `:next.jdbc/update-count`.
|
||||
* Fix #67 by adding `:jdbcUrl` version spec.
|
||||
* Add `next.jdbc.optional/as-maps-adapter` to provide a way to override the default result set reading behavior of using `.getObject` when omitting SQL `NULL` values from result set maps.
|
||||
|
||||
## Stable Builds
|
||||
|
||||
|
|
|
|||
|
|
@ -91,3 +91,41 @@
|
|||
and nil columns omitted."
|
||||
[rs opts]
|
||||
(as-unqualified-modified-maps rs (assoc opts :label-fn lower-case)))
|
||||
|
||||
(defn as-maps-adapter
|
||||
"Given a map builder function (e.g., `as-lower-maps`) 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.
|
||||
|
||||
This adapter omits SQL NULL values.
|
||||
|
||||
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.
|
||||
|
||||
`read-column-by-index` is still called on the result of that read, if
|
||||
it is not `nil`."
|
||||
[builder-fn column-reader]
|
||||
(fn [rs opts]
|
||||
(let [mrsb (builder-fn rs opts)]
|
||||
(reify
|
||||
rs/RowBuilder
|
||||
(->row [this] (rs/->row mrsb))
|
||||
(column-count [this] (rs/column-count mrsb))
|
||||
(with-column [this row i]
|
||||
(let [v (column-reader rs (:rsmeta mrsb) i)]
|
||||
(if (nil? v)
|
||||
row
|
||||
(assoc! row
|
||||
(nth (:cols mrsb) (dec i))
|
||||
(rs/read-column-by-index v (:rsmeta mrsb) i)))))
|
||||
(row! [this row] (rs/row! mrsb row))
|
||||
rs/ResultSetBuilder
|
||||
(->rs [this] (rs/->rs mrsb))
|
||||
(with-row [this mrs row] (rs/with-row mrsb mrs row))
|
||||
(rs! [this mrs] (rs/rs! mrsb mrs))))))
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||
[next.jdbc.optional :as opt]
|
||||
[next.jdbc.protocols :as p]
|
||||
[next.jdbc.test-fixtures :refer [with-test-db ds postgres?]]))
|
||||
[next.jdbc.test-fixtures :refer [with-test-db ds postgres?]])
|
||||
(:import (java.sql ResultSet ResultSetMetaData)))
|
||||
|
||||
(set! *warn-on-reflection* true)
|
||||
|
||||
|
|
@ -54,3 +55,60 @@
|
|||
(is (not (contains? row (if (postgres?) :fruit/appearance :FRUIT/appearance))))
|
||||
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/id) row)))
|
||||
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/name) row))))))
|
||||
|
||||
(defn- default-column-reader
|
||||
[^ResultSet rs ^ResultSetMetaData rsmeta ^Integer i]
|
||||
(.getObject rs i))
|
||||
|
||||
(deftest test-map-row-adapter
|
||||
(testing "default row builder"
|
||||
(let [row (p/-execute-one (ds)
|
||||
["select * from fruit where id = ?" 1]
|
||||
{:builder-fn (opt/as-maps-adapter
|
||||
opt/as-maps
|
||||
default-column-reader)})]
|
||||
(is (map? row))
|
||||
(is (not (contains? row (if (postgres?) :fruit/grade :FRUIT/GRADE))))
|
||||
(is (= 1 ((if (postgres?) :fruit/id :FRUIT/ID) row)))
|
||||
(is (= "Apple" ((if (postgres?) :fruit/name :FRUIT/NAME) row)))))
|
||||
(testing "unqualified row builder"
|
||||
(let [row (p/-execute-one (ds)
|
||||
["select * from fruit where id = ?" 2]
|
||||
{:builder-fn (opt/as-maps-adapter
|
||||
opt/as-unqualified-maps
|
||||
default-column-reader)})]
|
||||
(is (map? row))
|
||||
(is (not (contains? row (if (postgres?) :cost :COST))))
|
||||
(is (= 2 ((if (postgres?) :id :ID) row)))
|
||||
(is (= "Banana" ((if (postgres?) :name :NAME) row)))))
|
||||
(testing "lower-case row builder"
|
||||
(let [row (p/-execute-one (ds)
|
||||
["select * from fruit where id = ?" 3]
|
||||
{:builder-fn (opt/as-maps-adapter
|
||||
opt/as-lower-maps
|
||||
default-column-reader)})]
|
||||
(is (map? row))
|
||||
(is (not (contains? row :fruit/appearance)))
|
||||
(is (= 3 (:fruit/id row)))
|
||||
(is (= "Peach" (:fruit/name row)))))
|
||||
(testing "unqualified lower-case row builder"
|
||||
(let [row (p/-execute-one (ds)
|
||||
["select * from fruit where id = ?" 4]
|
||||
{:builder-fn (opt/as-maps-adapter
|
||||
opt/as-unqualified-lower-maps
|
||||
default-column-reader)})]
|
||||
(is (map? row))
|
||||
(is (= 4 (:id row)))
|
||||
(is (= "Orange" (:name row)))))
|
||||
(testing "custom row builder"
|
||||
(let [row (p/-execute-one (ds)
|
||||
["select * from fruit where id = ?" 3]
|
||||
{:builder-fn (opt/as-maps-adapter
|
||||
opt/as-modified-maps
|
||||
default-column-reader)
|
||||
:label-fn str/lower-case
|
||||
:qualifier-fn identity})]
|
||||
(is (map? row))
|
||||
(is (not (contains? row (if (postgres?) :fruit/appearance :FRUIT/appearance))))
|
||||
(is (= 3 ((if (postgres?) :fruit/id :FRUIT/id) row)))
|
||||
(is (= "Peach" ((if (postgres?) :fruit/name :FRUIT/name) row))))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue