Alpha 9; fixes #14; improves protocol docstrings

* Move documentation from `ns` into `defprotocol` and the method 
declarations.
* Indicate which protocols may be extended via metadata 
(`SettableParameter` and `Sourceable`).
This commit is contained in:
Sean Corfield 2019-04-22 17:41:31 -07:00
parent a8897765fa
commit 2c018654d3
7 changed files with 95 additions and 81 deletions

View file

@ -1,3 +1,4 @@
# Change Log
2019-04-21 -- 1.0.0-alpha8 -- Initial publicly announced release.
* 2019-04-22 -- 1.0.0-alpha9 -- Fix #14 by respecting `:gen-fn` in `execute-one` for `PreparedStatement`.
* 2019-04-21 -- 1.0.0-alpha8 -- Initial publicly announced release.

View file

@ -9,12 +9,12 @@ It is designed to work with Clojure 1.10 or later, supports `datafy`/`nav`, and
You can add `next.jdbc` to your project with either:
```clojure
{seancorfield/next.jdbc {:mvn/version "1.0.0-alpha8"}}
{seancorfield/next.jdbc {:mvn/version "1.0.0-alpha9"}}
```
for `deps.edn` or:
```clojure
[seancorfield/next.jdbc "1.0.0-alpha8"]
[seancorfield/next.jdbc "1.0.0-alpha9"]
```
for `project.clj` or `build.boot`.
@ -29,7 +29,7 @@ For the examples in this documentation, we will use a local H2 database on disk,
```clojure
;; deps.edn
{:deps {org.clojure/clojure {:mvn/version "1.10.0"}
seancorfield/next.jdbc {:mvn/version "1.0.0-alpha8"}
seancorfield/next.jdbc {:mvn/version "1.0.0-alpha9"}
com.h2database/h2 {:mvn/version "1.4.197"}}}
```

View file

@ -4,7 +4,7 @@ This page attempts to list all of the differences between [clojure.java.jdbc](ht
## Conceptually
`clojure.java.jdbc` focuses heavily on a `db-spec` hash map to describe the various ways of interacting with the database and grew from very imperative origins that expose a lot of the JDBC API (multiple type of SQL execution, some operations returned hash maps, others update counts as integers, etc).
`clojure.java.jdbc` focuses heavily on a `db-spec` hash map to describe the various ways of interacting with the database and grew from very imperative origins that expose a lot of the JDBC API (multiple types of SQL execution, some operations returned hash maps, others update counts as integers, etc).
`next.jdbc` focuses on using protocols and native Java JDBC types where possible (for performance and simplicity) and strives to present a more modern Clojure API with namespace-qualified keywords in hash maps, reducible SQL operations as part of the primary API, and a streamlined set of SQL execution primitives. Execution always returns a hash map (for one result) or a vector of hash maps (for multiple results) -- even update counts are returned as if they were result sets.

View file

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>seancorfield</groupId>
<artifactId>next.jdbc</artifactId>
<version>1.0.0-alpha8</version>
<version>1.0.0-alpha9</version>
<name>next.jdbc</name>
<description>The next generation of clojure.java.jdbc: a new low-level Clojure wrapper for JDBC-based access to databases.</description>

View file

@ -20,8 +20,9 @@
(defprotocol SettableParameter :extend-via-metadata true
"Protocol for setting SQL parameters in statement objects, which
can convert from Clojure values. The default implementation just
calls `.setObject` on the parameter value. It can be extended to use other
methods of `PreparedStatement` to convert and set parameter values."
calls `.setObject` on the parameter value. It can be extended to
use other methods of `PreparedStatement` to convert and set parameter
values. Extension via metadata is supported."
(set-parameter [val stmt ix]
"Convert a Clojure value into a SQL value and store it as the ix'th
parameter in the given SQL statement object."))

View file

@ -3,59 +3,61 @@
(ns next.jdbc.protocols
"This is the extensible core of the next generation java.jdbc library.
`Sourceable` protocol:
`get-datasource` -- turn something into a `javax.sql.DataSource`; implementations
are provided for strings, hash maps (`db-spec` structures), and also a
`DataSource` (which just returns itself).
`Connectable` protocol:
`get-connection` -- create a new JDBC connection that should be closed when you
are finished with it; implementations are provided for `DataSource`,
`PreparedStatement`, and `Object`, on the assumption that an `Object`
can possibly be turned into a `DataSource`.
`Executable` protocol:
`-execute` -- given SQL and parameters, produce a 'reducible' that, when
reduced, executes the SQL and produces a `ResultSet` that can be processed;
implementations are provided for `Connection`, `DataSource`,
`PreparedStatement`, and `Object` (on the assumption that an `Object` can be
turned into a `DataSource` and therefore used to get a `Connection`).
`-execute-one` -- given SQL and parameters, executes the SQL and produces
the first row of the `ResultSet` as a datafiable hash map (by default);
implementations are provided for `Connection`, `DataSource`,
`PreparedStatement`, and `Object` (on the assumption that an `Object` can be
turned into a `DataSource` and therefore used to get a `Connection`).
`-execute-all` -- given SQL and parameters, executes the SQL and produces
either a vector of datafiable hash maps from the `ResultSet` (default)
or a vector of column names followed by vectors of row values;
implementations are provided for `Connection`, `DataSource`,
`PreparedStatement`, and `Object` (on the assumption that an `Object` can be
turned into a `DataSource` and therefore used to get a `Connection`).
`Preparable` protocol:
`prepare` -- given SQL and parameters, produce a `PreparedStatement` that can
be executed (by -execute above); implementation is provided for
`Connection` only.
`Transactable` protocol:
`-transact` -- given a function (presumably containing SQL operations),
run the function inside a SQL transaction; implementations are provided
for `Connection`, `DataSource`, and `Object` (on the assumption that an
`Object` can be turned into a `DataSource`).")
`Sourceable` -- for producing `javax.sql.DataSource` objects,
`Connectable` -- for producing new `java.sql.Connection` objects,
`Executable` -- for executing SQL operations,
`Preparable` -- for producing new `java.sql.PreparedStatement` objects,
`Transactable` -- for executing SQL operations transactionally.")
(set! *warn-on-reflection* true)
(defprotocol Sourceable :extend-via-metadata true
(get-datasource ^javax.sql.DataSource [this]))
"Protocol for producing a `javax.sql.DataSource`.
Implementations are provided for strings, hash maps (`db-spec` structures),
and also a `DataSource` (which just returns itself).
Extension via metadata is supported."
(get-datasource ^javax.sql.DataSource [this]
"Produce a `javax.sql.DataSource`."))
(defprotocol Connectable
(get-connection ^java.lang.AutoCloseable [this opts]))
"Protocol for producing a new JDBC connection that should be closed when you
are finished with it.
Implementations are provided for `DataSource`, `PreparedStatement`, and
`Object`, on the assumption that an `Object` can be turned into a `DataSource`."
(get-connection ^java.lang.AutoCloseable [this opts]
"Produce a new `java.sql.Connection` for use with `with-open`."))
(defprotocol Executable
(-execute ^clojure.lang.IReduceInit [this sql-params opts])
(-execute-one [this sql-params opts])
(-execute-all [this sql-params opts]))
"Protocol for executing SQL operations.
Implementations are provided for `Connection`, `DataSource`,
`PreparedStatement`, and `Object`, on the assumption that an `Object` can be
turned into a `DataSource` and therefore used to get a `Connection`."
(-execute ^clojure.lang.IReduceInit [this sql-params opts]
"Produce a 'reducible' that, when reduced, executes the SQL and
processes the rows of the `ResultSet` directly.")
(-execute-one [this sql-params opts]
"Executes the SQL and produces the first row of the `ResultSet`
as a fully-realized, datafiable hash map (by default).")
(-execute-all [this sql-params opts]
"Executes the SQL and produces (by default) a vector of
fully-realized, datafiable hash maps from the `ResultSet`."))
(defprotocol Preparable
(prepare ^java.sql.PreparedStatement [this sql-params opts]))
"Protocol for producing a new `java.sql.PreparedStatement` that should
be closed after use. Can be used by `Executable` functions.
Implementation is provided for `Connection` only."
(prepare ^java.sql.PreparedStatement [this sql-params opts]
"Produce a new `java.sql.PreparedStatement` for use with `with-open`."))
(defprotocol Transactable
(-transact [this body-fn opts]))
"Protocol for running SQL operations in a transaction.
Implementations are provided for `Connection`, `DataSource`, and `Object`
(on the assumption that an `Object` can be turned into a `DataSource`)."
(-transact [this body-fn opts]
"Run the `body-fn` inside a transaction."))

View file

@ -76,30 +76,30 @@
(read-column-by-index [_1 _2 _3] nil))
(defprotocol RowBuilder
"Protocol for building rows in various representations:
`->row` -- called once per row to create the basis of each row
`column-count` -- return the number of columns in each row
`with-column` -- called with the row and the index of the column to be added;
this is expected to read the column value from the `ResultSet`!
`row!` -- called once per row to finalize each row once it is complete
"Protocol for building rows in various representations.
The default implementation for building hash maps: `MapResultSetBuilder`"
(->row [_])
(column-count [_])
(with-column [_ row i])
(row! [_ row]))
(->row [_]
"Called once per row to create the basis of each row.")
(column-count [_]
"Return the number of columns in each row.")
(with-column [_ row i]
"Called with the row and the index of the column to be added;
this is expected to read the column value from the `ResultSet`!")
(row! [_ row]
"Called once per row to finalize each row once it is complete."))
(defprotocol ResultSetBuilder
"Protocol for building result sets in various representations:
`->rs` -- called to create the basis of the result set
`with-row` -- called with the result set and the row to be added
`rs!` -- called to finalize the result set once it is complete
"Protocol for building result sets in various representations.
Default implementations for building vectors of hash maps and vectors
of column names and row values: `MapResultSetBuilder` & `ArrayResultSetBuilder`"
(->rs [_])
(with-row [_ rs row])
(rs! [_ rs]))
Default implementations for building vectors of hash maps and vectors of
column names and row values: `MapResultSetBuilder` & `ArrayResultSetBuilder`"
(->rs [_]
"Called to create the basis of the result set.")
(with-row [_ rs row]
"Called with the result set and the row to be added.")
(rs! [_ rs]
"Called to finalize the result set once it is complete."))
(defrecord MapResultSetBuilder [^ResultSet rs rsmeta cols]
RowBuilder
@ -199,9 +199,17 @@
(declare navize-row)
(defprotocol DatafiableRow
"Given a connectable object, return a function that knows how to turn a row
into a datafiable object that can be `nav`igated."
(datafiable-row [this connectable opts]))
"Protocol for making rows datafiable and therefore navigable.
The default implementation just adds metadata so that `datafy` can be
called on the row, which will produce something that `nav` can be called
on, to lazily navigate through foreign key relationships into other tables.
If `datafiable-row` is called when reducing the result set produced by
`next.jdbc/reducible!`, the row is fully-realized from the `ResultSet`
first."
(datafiable-row [this connectable opts]
"Produce a datafiable representation of a row from a `ResultSet`."))
(defn- row-builder
"Given a `RowBuilder` -- a row materialization strategy -- produce a fully
@ -396,9 +404,11 @@
(reduce-stmt this f init (assoc opts :return-keys true)))))
(-execute-one [this _ opts]
(if-let [rs (stmt->result-set this (assoc opts :return-keys true))]
(let [gen-fn (get opts :gen-fn as-maps)
gen (gen-fn rs opts)]
(when (.next rs)
(datafiable-row (row-builder (as-maps rs opts))
(.getConnection this) opts))
(datafiable-row (row-builder gen)
(.getConnection this) opts)))
{:next.jdbc/update-count (.getUpdateCount this)}))
(-execute-all [this _ opts]
(if-let [rs (stmt->result-set this opts)]