Address #70 by adding/documenting CLOB column reader example
This commit is contained in:
parent
6482d38960
commit
61083eba69
5 changed files with 56 additions and 38 deletions
|
|
@ -6,6 +6,7 @@ Only accretive/fixative changes will be made from now on.
|
||||||
|
|
||||||
The following changes have been committed to the **master** branch since the 1.0.9 release:
|
The following changes have been committed to the **master** branch since the 1.0.9 release:
|
||||||
|
|
||||||
|
* Address #70 by adding `next.jdbc.result-set/clob-column-reader` and `next.jdbc.result-set/clob->string` helper to make it easier to deal with `CLOB` column data.
|
||||||
* Update `org.clojure/java.data` to `"0.1.4"` (0.1.2 fixes a number of reflection warnings).
|
* Update `org.clojure/java.data` to `"0.1.4"` (0.1.2 fixes a number of reflection warnings).
|
||||||
|
|
||||||
## Stable Builds
|
## Stable Builds
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,14 @@ And finally there are adapters for the existing builders that let you override t
|
||||||
* `as-maps-adapter` -- adapts an existing map builder function with a new column reader,
|
* `as-maps-adapter` -- adapts an existing map builder function with a new column reader,
|
||||||
* `as-arrays-adapter` -- adapts an existing array builder function with a new column reader.
|
* `as-arrays-adapter` -- adapts an existing array builder function with a new column reader.
|
||||||
|
|
||||||
|
An example column reader is provided -- `clob-column-reader` -- that still uses `.getObject` but will expand `java.sql.Clob` values into string (using the `clob->string` helper function):
|
||||||
|
|
||||||
|
```clojure
|
||||||
|
{:builder-fn (result-set/as-maps-adapter
|
||||||
|
result-set/as-maps
|
||||||
|
result-set/clob-column-reader)}
|
||||||
|
```
|
||||||
|
|
||||||
## RowBuilder Protocol
|
## RowBuilder Protocol
|
||||||
|
|
||||||
This protocol defines four functions and is used whenever `next.jdbc` needs to materialize a row from a `ResultSet` as a Clojure data structure:
|
This protocol defines four functions and is used whenever `next.jdbc` needs to materialize a row from a `ResultSet` as a Clojure data structure:
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,11 @@
|
||||||
Also provides the default implemenations for `Executable` and
|
Also provides the default implemenations for `Executable` and
|
||||||
the default `datafy`/`nav` behavior for rows from a result set."
|
the default `datafy`/`nav` behavior for rows from a result set."
|
||||||
(:require [clojure.core.protocols :as core-p]
|
(:require [clojure.core.protocols :as core-p]
|
||||||
|
[clojure.java.io :as io]
|
||||||
[next.jdbc.prepare :as prepare]
|
[next.jdbc.prepare :as prepare]
|
||||||
[next.jdbc.protocols :as p])
|
[next.jdbc.protocols :as p])
|
||||||
(:import (java.sql PreparedStatement
|
(:import (java.sql Clob
|
||||||
|
PreparedStatement
|
||||||
ResultSet ResultSetMetaData
|
ResultSet ResultSetMetaData
|
||||||
SQLException)
|
SQLException)
|
||||||
(java.util Locale)))
|
(java.util Locale)))
|
||||||
|
|
@ -236,6 +238,21 @@
|
||||||
(with-row [this mrs row] (with-row mrsb mrs row))
|
(with-row [this mrs row] (with-row mrsb mrs row))
|
||||||
(rs! [this mrs] (rs! mrsb mrs))))))
|
(rs! [this mrs] (rs! mrsb mrs))))))
|
||||||
|
|
||||||
|
(defn clob->string
|
||||||
|
"Given a CLOB column value, read it as a string."
|
||||||
|
[^Clob clob]
|
||||||
|
(with-open [rdr (io/reader (.getCharacterStream clob))]
|
||||||
|
(slurp rdr)))
|
||||||
|
|
||||||
|
(defn clob-column-reader
|
||||||
|
"An example column-reader that still uses `.getObject` but expands CLOB
|
||||||
|
columns into strings."
|
||||||
|
[^ResultSet rs ^ResultSetMetaData _ ^Integer i]
|
||||||
|
(when-let [value (.getObject rs i)]
|
||||||
|
(cond-> value
|
||||||
|
(instance? Clob value)
|
||||||
|
(clob->string))))
|
||||||
|
|
||||||
(defrecord ArrayResultSetBuilder [^ResultSet rs rsmeta cols]
|
(defrecord ArrayResultSetBuilder [^ResultSet rs rsmeta cols]
|
||||||
RowBuilder
|
RowBuilder
|
||||||
(->row [this] (transient []))
|
(->row [this] (transient []))
|
||||||
|
|
|
||||||
|
|
@ -289,3 +289,30 @@
|
||||||
(= "fruit" (-> % val name str/lower-case)))
|
(= "fruit" (-> % val name str/lower-case)))
|
||||||
row))
|
row))
|
||||||
metadata))))
|
metadata))))
|
||||||
|
|
||||||
|
(deftest clob-reading
|
||||||
|
(when-not (postgres?) ; embedded postgres does not support clob
|
||||||
|
(with-open [con (p/get-connection (ds) {})]
|
||||||
|
(try
|
||||||
|
(p/-execute-one con ["DROP TABLE CLOBBER"] {})
|
||||||
|
(catch Exception _))
|
||||||
|
(p/-execute-one con [(str "
|
||||||
|
CREATE TABLE CLOBBER (
|
||||||
|
ID INTEGER,
|
||||||
|
STUFF CLOB
|
||||||
|
)")]
|
||||||
|
{})
|
||||||
|
(p/-execute-one con
|
||||||
|
[(str "insert into clobber (id, stuff)"
|
||||||
|
"values (?,?), (?,?)")
|
||||||
|
1 "This is some long string"
|
||||||
|
2 "This is another long string"]
|
||||||
|
{})
|
||||||
|
(is (= "This is some long string"
|
||||||
|
(-> (p/-execute-all con
|
||||||
|
["select * from clobber where id = ?" 1]
|
||||||
|
{:builder-fn (rs/as-maps-adapter
|
||||||
|
rs/as-unqualified-lower-maps
|
||||||
|
rs/clob-column-reader)})
|
||||||
|
(first)
|
||||||
|
:stuff))))))
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
(ns next.jdbc-test
|
(ns next.jdbc-test
|
||||||
"Not exactly a test suite -- more a series of examples."
|
"Not exactly a test suite -- more a series of examples."
|
||||||
(:require [clojure.java.io :as io]
|
(:require [clojure.string :as str]
|
||||||
[clojure.string :as str]
|
|
||||||
[clojure.test :refer [deftest is testing use-fixtures]]
|
[clojure.test :refer [deftest is testing use-fixtures]]
|
||||||
[next.jdbc :as jdbc]
|
[next.jdbc :as jdbc]
|
||||||
[next.jdbc.connection :as c]
|
[next.jdbc.connection :as c]
|
||||||
|
|
@ -12,7 +11,7 @@
|
||||||
[next.jdbc.prepare :as prep]
|
[next.jdbc.prepare :as prep]
|
||||||
[next.jdbc.result-set :as rs]
|
[next.jdbc.result-set :as rs]
|
||||||
[next.jdbc.specs :as specs])
|
[next.jdbc.specs :as specs])
|
||||||
(:import (java.sql Clob ResultSet ResultSetMetaData)))
|
(:import (java.sql ResultSet ResultSetMetaData)))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
|
|
@ -213,37 +212,3 @@ VALUES ('Pear', 'green', 49, 47)
|
||||||
(into [] (map pr-str) (jdbc/plan (ds) ["select * from fruit"]))))
|
(into [] (map pr-str) (jdbc/plan (ds) ["select * from fruit"]))))
|
||||||
(is (thrown? IllegalArgumentException
|
(is (thrown? IllegalArgumentException
|
||||||
(doall (take 3 (jdbc/plan (ds) ["select * from fruit"]))))))
|
(doall (take 3 (jdbc/plan (ds) ["select * from fruit"]))))))
|
||||||
|
|
||||||
(defn- clob-reader
|
|
||||||
[^ResultSet rs ^ResultSetMetaData _ ^Integer i]
|
|
||||||
(let [obj (.getObject rs i)]
|
|
||||||
(cond (instance? Clob obj)
|
|
||||||
(with-open [rdr (io/reader (.getCharacterStream ^Clob obj))]
|
|
||||||
(slurp rdr))
|
|
||||||
:default
|
|
||||||
obj)))
|
|
||||||
|
|
||||||
(deftest clob-reading
|
|
||||||
(when-not (postgres?)
|
|
||||||
(with-open [con (jdbc/get-connection (ds))]
|
|
||||||
(try
|
|
||||||
(jdbc/execute-one! con ["DROP TABLE CLOBBER"])
|
|
||||||
(catch Exception _))
|
|
||||||
(jdbc/execute-one! con [(str "
|
|
||||||
CREATE TABLE CLOBBER (
|
|
||||||
ID INTEGER,
|
|
||||||
STUFF CLOB
|
|
||||||
)")])
|
|
||||||
(jdbc/execute-one! con
|
|
||||||
[(str "insert into clobber (id, stuff)"
|
|
||||||
"values (?,?), (?,?)")
|
|
||||||
1 "This is some long string"
|
|
||||||
2 "This is another long string"])
|
|
||||||
(is (= "This is some long string"
|
|
||||||
(-> (jdbc/execute! con
|
|
||||||
["select * from clobber where id = ?" 1]
|
|
||||||
{:builder-fn (rs/as-maps-adapter
|
|
||||||
rs/as-unqualified-lower-maps
|
|
||||||
clob-reader)})
|
|
||||||
(first)
|
|
||||||
:stuff))))))
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue