Fixes #48 by documenting connection/->pool

This commit is contained in:
Sean Corfield 2019-07-18 12:12:09 -07:00
parent 697e4a840f
commit b3f462e90b
2 changed files with 64 additions and 6 deletions

View file

@ -22,7 +22,7 @@ In addition, you will need to add dependencies for the JDBC drivers you wish to
## An Example REPL Session ## An Example REPL Session
To start using `next.jdbc`, you need to create a datasource (an instance of `javax.sql.DataSource`). You can use `next.jdbc/get-datasource` with either a "db-spec" -- a hash map describing the database you wish to connect to -- or a JDBC URI string. Or you can construct a datasource from one of the connection pooling libraries out there, such as [HikariCP](https://brettwooldridge.github.io/HikariCP/) or [c3p0](https://www.mchange.com/projects/c3p0/). To start using `next.jdbc`, you need to create a datasource (an instance of `javax.sql.DataSource`). You can use `next.jdbc/get-datasource` with either a "db-spec" -- a hash map describing the database you wish to connect to -- or a JDBC URI string. Or you can construct a datasource from one of the connection pooling libraries out there, such as [HikariCP](https://brettwooldridge.github.io/HikariCP/) or [c3p0](https://www.mchange.com/projects/c3p0/) -- see [Connection Pooling](#connection-pooling) below.
For the examples in this documentation, we will use a local H2 database on disk, and we'll use the [Clojure CLI tools](https://clojure.org/guides/deps_and_cli) and `deps.edn`: For the examples in this documentation, we will use a local H2 database on disk, and we'll use the [Clojure CLI tools](https://clojure.org/guides/deps_and_cli) and `deps.edn`:
@ -113,7 +113,7 @@ Any operation that can perform key-based lookup can be used here without creatin
## Datasources, Connections & Transactions ## Datasources, Connections & Transactions
In the examples above, we created a datasource and then passed it into each function call. When `next.jdbc` is given a datasource, it creates a `java.sql.Connection` from it, uses it for the SQL operation, and then closes it. If you're not using a connection pooling datasource, that can be quite an overhead: setting up database connections to remote servers is not cheap! In the examples above, we created a datasource and then passed it into each function call. When `next.jdbc` is given a datasource, it creates a `java.sql.Connection` from it, uses it for the SQL operation, and then closes it. If you're not using a connection pooling datasource (see below), that can be quite an overhead: setting up database connections to remote servers is not cheap!
If you want to run multiple SQL operations without that overhead each time, you can create the connection yourself and reuse it across several operations using `with-open` and `next.jdbc/get-connection`: If you want to run multiple SQL operations without that overhead each time, you can create the connection yourself and reuse it across several operations using `with-open` and `next.jdbc/get-connection`:
@ -145,6 +145,47 @@ If `with-transaction` is given a datasource, it will create and close the connec
(jdbc/execute! con ...)) ; committed (jdbc/execute! con ...)) ; committed
``` ```
## Connection Pooling
`next.jdbc` makes it easy to use either HikariCP or c3p0 for connection pooling.
First, you need to add the connection pooling library as a dependency, e.g.,
```clojure
com.zaxxer/HikariCP {:mvn/version "3.3.1"}
;; or:
com.mchange/c3p0 {:mvn/version "0.9.5.4"}
```
_Check those libraries' documentation for the latest version to use!_
Then import the appropriate classes into your code:
```clojure
(ns my.main
(:require [next.jdbc :as jdbc]
[next.jdbc.connection :as connection])
(:import (com.zaxxer.hikari HikariDataSource)
;; or:
(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).
```clojure
(let [^HikariDataSource ds (connection/->pool HikariDataSource db-spec)]
(jdbc/execute! ds ...)
(jdbc/execute! ds ...)
(into [] (map :column) (jdbc/plan ds ...)))
;; or:
(let [^PooledDataSource ds (connection/->pool ComboPooledDataSource db-spec)]
(jdbc/execute! ds ...)
(jdbc/execute! ds ...)
(into [] (map :column) (jdbc/plan ds ...)))
```
You only need the type hints on `ds` if you plan to call methods on it via Java interop, such as `.close` (or using `with-open` instead of `let` to auto-close it) and you want to avoid reflection.
## Support from Specs ## Support from Specs
As you are developing with `next.jdbc`, it can be useful to have assistance from `clojure.spec` in checking calls to `next.jdbc`'s functions, to provide explicit argument checking and/or better error messages for some common mistakes, e.g., trying to pass a plain SQL string where a vector (containing a SQL string, and no parameters) is expected. As you are developing with `next.jdbc`, it can be useful to have assistance from `clojure.spec` in checking calls to `next.jdbc`'s functions, to provide explicit argument checking and/or better error messages for some common mistakes, e.g., trying to pass a plain SQL string where a vector (containing a SQL string, and no parameters) is expected.

View file

@ -193,11 +193,28 @@
[url etc])) [url etc]))
(defn ->pool (defn ->pool
"Given a connection pooling class and a database spec, return an connection "Given a (connection pooled datasource) class and a database spec, return a
pool object built from the database spec." connection pool object built from that class and the database spec.
Assumes the `clazz` has a `.setJdbcUrl` method (which HikariCP and c3p0 do).
If you already have a JDBC URL and want to use this method, pass `:jdbcUrl`
in the database spec (instead of `:dbtype`, `:dbname`, etc).
Properties for the connection pool object can be passed as mixed case
keywords that correspond to setter methods (just as `:jdbcUrl` maps to
`.setJdbcUrl`). `clojure.java.data/to-java` is used to construct the
object and call the setters.
Note that the result is not type-hinted (because there's no common base
class or interface that can be assumed). In particular, connection pooled
datasource object may need to be closed but they don't necessarily implement
`java.io.Closeable` (HikariCP does, c3p0 does not)."
[clazz db-spec] [clazz db-spec]
(let [[url etc] (spec->url+etc db-spec)] (if (:jdbcUrl db-spec)
(to-java clazz (assoc etc :jdbcUrl url)))) (to-java clazz db-spec)
(let [[url etc] (spec->url+etc db-spec)]
(to-java clazz (assoc etc :jdbcUrl url)))))
(defn- string->url+etc (defn- string->url+etc
"Given a JDBC URL, return it with an empty set of options with no parsing." "Given a JDBC URL, return it with an empty set of options with no parsing."