Fixes #66 by adding support for :jdbcUrl in db-spec hash map

This commit is contained in:
Sean Corfield 2019-09-27 13:06:45 -07:00
parent 0f0f6fbe92
commit 6c42d7ce67
5 changed files with 84 additions and 55 deletions

View file

@ -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.7 release: The following changes have been committed to the **master** branch since the 1.0.7 release:
* Fix #66 by adding support for a db-spec hash map format containing a `:jdbcUrl` key (consistent with `->pool`) so that you can create a datasource from a JDBC URL string and additional options.
* Address #65 by adding a HugSQL "quick start" to the Friendly SQL Functions section of the docs. * Address #65 by adding a HugSQL "quick start" to the Friendly SQL Functions section of the docs.
* Add `next.jdbc.specs/unstrument`. PR #64 (@gerred). * Add `next.jdbc.specs/unstrument`. PR #64 (@gerred).
* Address #63 by improving documentation around qualified column names and `:qualifier` (`clojure.java.jdbc`) migration. * Address #63 by improving documentation around qualified column names and `:qualifier` (`clojure.java.jdbc`) migration.

View file

@ -68,6 +68,8 @@ We described the database with just `:dbtype` and `:dbname` because it is create
> Note: You can see the full list of `:dbtype` values supported in [next.jdbc/get-datasource](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/api/next.jdbc#get-datasource)'s docstring. If you need this programmatically, you can get it from the [next.jdbc.connection/dbtypes](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/api/next.jdbc.connection#dbtypes) hash map. If those lists differ, the hash map is the definitive list (and I'll need to fix the docstring!). The docstring of that Var explains how to tell `next.jdbc` about additional databases. > Note: You can see the full list of `:dbtype` values supported in [next.jdbc/get-datasource](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/api/next.jdbc#get-datasource)'s docstring. If you need this programmatically, you can get it from the [next.jdbc.connection/dbtypes](https://cljdoc.org/d/seancorfield/next.jdbc/CURRENT/api/next.jdbc.connection#dbtypes) hash map. If those lists differ, the hash map is the definitive list (and I'll need to fix the docstring!). The docstring of that Var explains how to tell `next.jdbc` about additional databases.
If you already have a JDBC URL (string), you can use that as-is instead of the db-spec hash map. If you have a JDBC URL and still need additional options passed into the JDBC driver, you can use a hash map with the `:jdbcUrl` key specifying the string and whatever additional options you need.
### `execute!` & `execute-one!` ### `execute!` & `execute-one!`
We used `execute!` to create the `address` table, to insert a new row into it, and to query it. In all three cases, `execute!` returns a vector of hash maps with namespace-qualified keys, representing the result set from the operation, if available. When no result set is produced, `next.jdbc` returns a "result set" containing the "update count" from the operation (which is usually the number of rows affected). By default, H2 uses uppercase names and `next.jdbc` returns these as-is. We used `execute!` to create the `address` table, to insert a new row into it, and to query it. In all three cases, `execute!` returns a vector of hash maps with namespace-qualified keys, representing the result set from the operation, if available. When no result set is produced, `next.jdbc` returns a "result set" containing the "update count" from the operation (which is usually the number of rows affected). By default, H2 uses uppercase names and `next.jdbc` returns these as-is.
@ -172,7 +174,7 @@ Then import the appropriate classes into your code:
(com.mchange.v2.c3p0 ComboPooledDataSource PooledDataSource))) (com.mchange.v2.c3p0 ComboPooledDataSource PooledDataSource)))
``` ```
Finally, create the connection pooled datasource. `db-spec` here contains the regular `next.jdbc` options (`:dbtype`, `:dbname`, and maybe `:host`, `:port`, `:classname` etc). Those are used to construct the JDBC URL that is passed into the datasource object (by calling `.setJdbcUrl` on it). You can also specify any of the connection pooling library's options, as mixed case keywords corresponding to any simple setter methods on the class being passed in, e.g., `:connectionTestQuery`, `:maximumPoolSize` (HikariCP), `:maxPoolSize`, `:preferredTestQuery` (c3p0). Finally, create the connection pooled datasource. `db-spec` here contains the regular `next.jdbc` options (`:dbtype`, `:dbname`, and maybe `:host`, `:port`, `:classname` etc -- or the `:jdbcUrl` format mentioned above). Those are used to construct the JDBC URL that is passed into the datasource object (by calling `.setJdbcUrl` on it). You can also specify any of the connection pooling library's options, as mixed case keywords corresponding to any simple setter methods on the class being passed in, e.g., `:connectionTestQuery`, `:maximumPoolSize` (HikariCP), `:maxPoolSize`, `:preferredTestQuery` (c3p0).
Some important notes regarding HikariCP: Some important notes regarding HikariCP:

View file

@ -58,7 +58,11 @@
"Given some sort of specification of a database, return a `DataSource`. "Given some sort of specification of a database, return a `DataSource`.
A specification can be a JDBC URL string (which is passed to the JDBC A specification can be a JDBC URL string (which is passed to the JDBC
driver as-is), or a hash map. For the hash map, these keys are required: driver as-is), or a hash map.
For the hash map, there are two formats accepted:
In the first format, these keys are required:
* `:dbtype` -- a string indicating the type of the database * `:dbtype` -- a string indicating the type of the database
* `:dbname` -- a string indicating the name of the database to be used * `:dbname` -- a string indicating the name of the database to be used
@ -81,10 +85,13 @@
* `:host-prefix` -- override the `//` that normally precedes the IP * `:host-prefix` -- override the `//` that normally precedes the IP
address or hostname in the JDBC URL address or hostname in the JDBC URL
In the second format, this key is required:
* `:jdbcUrl` -- a JDBC URL string
Any additional options provided will be passed to the JDBC driver's Any additional options provided will be passed to the JDBC driver's
`.getConnection` call as a `java.util.Properties` structure. `.getConnection` call as a `java.util.Properties` structure.
Database types supported, and their defaults: Database types supported (for `:dbtype`), and their defaults:
* `derby` -- `org.apache.derby.jdbc.EmbeddedDriver` -- also pass `:create true` * `derby` -- `org.apache.derby.jdbc.EmbeddedDriver` -- also pass `:create true`
if you want the database to be automatically created if you want the database to be automatically created
* `h2` -- `org.h2.Driver` -- for an on-disk database * `h2` -- `org.h2.Driver` -- for an on-disk database

View file

@ -136,10 +136,20 @@
(atom {})) (atom {}))
(defn- spec->url+etc (defn- spec->url+etc
"Given a database spec, return a JDBC URL and a map of any additional options." "Given a database spec, return a JDBC URL and a map of any additional options.
As a special case, the database spec can contain jdbcUrl (just like ->pool),
in which case it will return that URL as-is and a map of any other options."
[{:keys [dbtype dbname host port classname [{:keys [dbtype dbname host port classname
dbname-separator host-prefix] dbname-separator host-prefix
jdbcUrl]
:as db-spec}] :as db-spec}]
(let [etc (dissoc db-spec
:dbtype :dbname :host :port :classname
:dbname-separator :host-prefix
:jdbcUrl)]
(if jdbcUrl
[jdbcUrl etc]
(let [;; allow aliases for dbtype (let [;; allow aliases for dbtype
subprotocol (-> dbtype dbtypes :alias-for (or dbtype)) subprotocol (-> dbtype dbtypes :alias-for (or dbtype))
host (or host (-> dbtype dbtypes :host) "127.0.0.1") host (or host (-> dbtype dbtypes :host) "127.0.0.1")
@ -165,10 +175,7 @@
(or host-prefix (-> dbtype dbtypes :host-prefix (or "//"))) (or host-prefix (-> dbtype dbtypes :host-prefix (or "//")))
host host
(when port (str ":" port)) (when port (str ":" port))
db-sep dbname)) db-sep dbname))]
etc (dissoc db-spec
:dbtype :dbname :host :port :classname
:dbname-separator :host-prefix)]
;; verify the datasource is loadable ;; verify the datasource is loadable
(if-let [class-name (or classname (-> dbtype dbtypes :classname))] (if-let [class-name (or classname (-> dbtype dbtypes :classname))]
(swap! driver-cache update class-name (swap! driver-cache update class-name
@ -190,7 +197,7 @@
(throw loaded)) (throw loaded))
loaded))))))) loaded)))))))
(throw (ex-info (str "Unknown dbtype: " dbtype) db-spec))) (throw (ex-info (str "Unknown dbtype: " dbtype) db-spec)))
[url etc])) [url etc]))))
(defn ->pool (defn ->pool
"Given a (connection pooled datasource) class and a database spec, return a "Given a (connection pooled datasource) class and a database spec, return a

View file

@ -103,6 +103,18 @@
(is (str/index-of (pr-str ds) url)) (is (str/index-of (pr-str ds) url))
(with-open [con (p/get-connection ds {})] (with-open [con (p/get-connection ds {})]
(is (instance? java.sql.Connection con))))) (is (instance? java.sql.Connection con)))))
(testing "datasource via jdbcUrl"
(let [[url etc] (#'c/spec->url+etc db)
ds (p/get-datasource (assoc etc :jdbcUrl url))]
(if (= "derby" (:dbtype db))
(is {:create true} etc)
(is {} etc))
(is (instance? javax.sql.DataSource ds))
(is (str/index-of (pr-str ds) (str "jdbc:" (:dbtype db))))
;; checks get-datasource on a DataSource is identity
(is (identical? ds (p/get-datasource ds)))
(with-open [con (p/get-connection ds {})]
(is (instance? java.sql.Connection con)))))
(testing "datasource via HikariCP" (testing "datasource via HikariCP"
;; the type hint is only needed because we want to call .close ;; the type hint is only needed because we want to call .close
(with-open [^HikariDataSource ds (c/->pool HikariDataSource db)] (with-open [^HikariDataSource ds (c/->pool HikariDataSource db)]