2019-01-08 07:03:20 +00:00
|
|
|
;; copyright (c) 2018-2019 Sean Corfield, all rights reserved
|
2019-01-08 04:38:58 +00:00
|
|
|
|
2019-01-08 07:03:20 +00:00
|
|
|
(ns next.jdbc
|
2019-04-01 06:17:12 +00:00
|
|
|
"The public API of the next generation java.jdbc library.
|
|
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
The basic building blocks are the `java.sql`/`javax.sql` classes:
|
|
|
|
|
* `DataSource` -- something to get connections from,
|
|
|
|
|
* `Connection` -- an active connection to the database,
|
|
|
|
|
* `PreparedStatement` -- SQL and parameters combined, from a connection,
|
2019-04-18 20:19:59 +00:00
|
|
|
|
|
|
|
|
and the following functions and a macro:
|
2019-05-22 23:22:14 +00:00
|
|
|
* `plan` -- given a connectable and SQL + parameters or a statement,
|
2019-04-01 06:17:12 +00:00
|
|
|
return a reducible that, when reduced will execute the SQL and consume
|
2019-04-21 23:13:52 +00:00
|
|
|
the `ResultSet` produced,
|
|
|
|
|
* `execute!` -- given a connectable and SQL + parameters or a statement,
|
|
|
|
|
execute the SQL, consume the `ResultSet` produced, and return a vector
|
2019-04-18 20:19:59 +00:00
|
|
|
of hash maps representing the rows (@1); this can be datafied to allow
|
|
|
|
|
navigation of foreign keys into other tables (either by convention or
|
|
|
|
|
via a schema definition),
|
2019-04-21 23:13:52 +00:00
|
|
|
* `execute-one!` -- given a connectable and SQL + parameters or a statement,
|
|
|
|
|
execute the SQL, consume the first row of the `ResultSet` produced, and
|
2019-04-18 20:19:59 +00:00
|
|
|
return a hash map representing that row; this can be datafied to allow
|
2019-04-01 06:17:12 +00:00
|
|
|
navigation of foreign keys into other tables (either by convention or
|
2019-04-18 20:19:59 +00:00
|
|
|
via a schema definition),
|
2019-04-21 23:13:52 +00:00
|
|
|
* `prepare` -- given a `Connection` and SQL + parameters, construct a new
|
|
|
|
|
`PreparedStatement`; in general this should be used with `with-open`,
|
|
|
|
|
* `transact` -- the functional implementation of `with-transaction`,
|
|
|
|
|
* `with-transaction` -- execute a series of SQL operations within a transaction.
|
2019-04-01 06:17:12 +00:00
|
|
|
|
2019-04-18 20:19:59 +00:00
|
|
|
@1 result sets are built, by default, as vectors of hash maps, containing
|
|
|
|
|
qualified keywords as column names, but the row builder and result set
|
|
|
|
|
builder machinery is open and alternatives are provided to produce
|
|
|
|
|
unqualified keywords as column names, and to produce a vector the
|
2019-04-21 23:13:52 +00:00
|
|
|
column names followed by vectors of column values for each row, and
|
|
|
|
|
lower-case variants of each.
|
|
|
|
|
|
|
|
|
|
The following options are supported wherever a `PreparedStatement` is created:
|
|
|
|
|
* `:concurrency` -- `:read-only`, `:updatable`,
|
|
|
|
|
* `:cursors` -- `:close`, `:hold`
|
|
|
|
|
* `:fetch-size` -- the fetch size value,
|
|
|
|
|
* `:max-rows` -- the maximum number of rows to return,
|
|
|
|
|
* `:result-type` -- `:forward-only`, `:scroll-insensitive`, `:scroll-sensitive`,
|
|
|
|
|
* `:return-keys` -- either `true` or a vector of key names to return,
|
|
|
|
|
* `:timeout` -- the query timeout."
|
2019-06-08 00:39:58 +00:00
|
|
|
(:require [next.jdbc.connection]
|
|
|
|
|
[next.jdbc.prepare]
|
2019-03-31 23:54:34 +00:00
|
|
|
[next.jdbc.protocols :as p]
|
2019-06-08 00:39:58 +00:00
|
|
|
[next.jdbc.result-set]
|
|
|
|
|
[next.jdbc.transaction]))
|
2019-01-08 07:03:20 +00:00
|
|
|
|
|
|
|
|
(set! *warn-on-reflection* true)
|
|
|
|
|
|
2019-04-01 06:17:12 +00:00
|
|
|
(defn get-datasource
|
2019-04-21 23:13:52 +00:00
|
|
|
"Given some sort of specification of a database, return a `DataSource`.
|
2019-04-02 04:31:38 +00:00
|
|
|
|
|
|
|
|
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:
|
2019-04-21 23:13:52 +00:00
|
|
|
* `:dbtype` -- a string indicating the type of the database
|
|
|
|
|
* `:dbname` -- a string indicating the name of the database to be used
|
2019-04-02 04:31:38 +00:00
|
|
|
|
|
|
|
|
The following optional keys are commonly used:
|
2019-04-21 23:13:52 +00:00
|
|
|
* `:user` -- the username to authenticate with
|
|
|
|
|
* `:password` -- the password to authenticate with
|
|
|
|
|
* `:host` -- the hostname or IP address of the database (default: `127.0.0.1`)
|
|
|
|
|
* `:port` -- the port for the database connection (the default is database-
|
2019-04-02 04:31:38 +00:00
|
|
|
specific -- see below)
|
2019-04-21 23:13:52 +00:00
|
|
|
* `:classname` -- if you need to override the default for the `:dbtype`
|
2019-04-02 04:31:38 +00:00
|
|
|
(or you want to use a database that next.jdbc does not know about!)
|
|
|
|
|
|
|
|
|
|
Any additional options provided will be passed to the JDBC driver's
|
2019-04-21 23:13:52 +00:00
|
|
|
`.getConnection` call as a `java.util.Properties` structure.
|
2019-04-02 04:31:38 +00:00
|
|
|
|
|
|
|
|
Database types supported, and their defaults:
|
2019-04-21 23:13:52 +00:00
|
|
|
* `derby` -- `org.apache.derby.jdbc.EmbeddedDriver` -- also pass `:create true`
|
2019-04-02 04:31:38 +00:00
|
|
|
if you want the database to be automatically created
|
2019-04-21 23:13:52 +00:00
|
|
|
* `h2` -- `org.h2.Driver` -- for an on-disk database
|
|
|
|
|
* `h2:mem` -- `org.h2.Driver` -- for an in-memory database
|
|
|
|
|
* `hsqldb`, `hsql` -- `org.hsqldb.jdbcDriver`
|
|
|
|
|
* `jtds:sqlserver`, `jtds` -- `net.sourceforge.jtds.jdbc.Driver` -- `1433`
|
|
|
|
|
* `mysql` -- `com.mysql.cj.jdbc.Driver`, `com.mysql.jdbc.Driver` -- `3306`
|
|
|
|
|
* `oracle:oci` -- `oracle.jdbc.OracleDriver` -- `1521`
|
|
|
|
|
* `oracle:thin`, `oracle` -- `oracle.jdbc.OracleDriver` -- `1521`
|
|
|
|
|
* `oracle:sid` -- `oracle.jdbc.OracleDriver` -- `1521` -- uses the legacy `:`
|
|
|
|
|
separator for the database name but otherwise behaves like `oracle:thin`
|
|
|
|
|
* `postgresql`, `postgres` -- `org.postgresql.Driver` -- `5432`
|
|
|
|
|
* `pgsql` -- `com.impossibl.postgres.jdbc.PGDriver` -- no default port
|
|
|
|
|
* `redshift` -- `com.amazon.redshift.jdbc.Driver` -- no default port
|
|
|
|
|
* `sqlite` -- `org.sqlite.JDBC`
|
2019-07-11 02:32:24 +00:00
|
|
|
* `sqlserver`, `mssql` -- `com.microsoft.sqlserver.jdbc.SQLServerDriver` -- `1433`
|
|
|
|
|
* `timesten:client` -- `com.timesten.jdbc.TimesTenClientDriver`
|
|
|
|
|
* `timesten:direct` -- `com.timesten.jdbc.TimesTenDriver`"
|
2019-05-29 20:51:11 +00:00
|
|
|
^javax.sql.DataSource
|
2019-04-01 06:17:12 +00:00
|
|
|
[spec]
|
|
|
|
|
(p/get-datasource spec))
|
2019-01-11 03:23:35 +00:00
|
|
|
|
2019-04-01 06:17:12 +00:00
|
|
|
(defn get-connection
|
2019-04-21 23:13:52 +00:00
|
|
|
"Given some sort of specification of a database, return a new `Connection`.
|
2019-01-10 07:30:46 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
In general, this should be used via `with-open`:
|
2019-04-01 06:17:12 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
```clojure
|
2019-04-01 06:17:12 +00:00
|
|
|
(with-open [con (get-connection spec opts)]
|
2019-04-02 04:31:38 +00:00
|
|
|
(run-some-ops con))
|
2019-04-21 23:13:52 +00:00
|
|
|
```
|
2019-04-02 04:31:38 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
If you call `get-connection` on a `DataSource`, it just calls `.getConnection`
|
|
|
|
|
and applies the `:auto-commit` and/or `:read-only` options, if provided.
|
2019-04-02 04:31:38 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
If you call `get-connection` on anything else, it will call `get-datasource`
|
|
|
|
|
first to try to get a `DataSource`, and then call `get-connection` on that."
|
2019-05-29 20:51:11 +00:00
|
|
|
(^java.sql.Connection
|
|
|
|
|
[spec]
|
|
|
|
|
(p/get-connection spec {}))
|
|
|
|
|
(^java.sql.Connection
|
|
|
|
|
[spec opts]
|
|
|
|
|
(p/get-connection spec opts)))
|
2019-04-01 06:17:12 +00:00
|
|
|
|
|
|
|
|
(defn prepare
|
2019-04-02 04:31:38 +00:00
|
|
|
"Given a connection to a database, and a vector containing SQL and any
|
2019-04-21 23:13:52 +00:00
|
|
|
parameters it needs, return a new `PreparedStatement`.
|
2019-04-01 06:17:12 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
In general, this should be used via `with-open`:
|
2019-04-01 06:17:12 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
```clojure
|
2019-04-01 06:17:12 +00:00
|
|
|
(with-open [stmt (prepare spec sql-params opts)]
|
2019-04-02 04:31:38 +00:00
|
|
|
(run-some-ops stmt))
|
2019-04-21 23:13:52 +00:00
|
|
|
```
|
2019-04-02 04:31:38 +00:00
|
|
|
|
|
|
|
|
See the list of options above (in the namespace docstring) for what can
|
|
|
|
|
be passed to prepare."
|
2019-05-29 20:51:11 +00:00
|
|
|
(^java.sql.PreparedStatement
|
|
|
|
|
[connection sql-params]
|
|
|
|
|
(p/prepare connection sql-params {}))
|
|
|
|
|
(^java.sql.PreparedStatement
|
|
|
|
|
[connection sql-params opts]
|
|
|
|
|
(p/prepare connection sql-params opts)))
|
2019-01-08 07:03:20 +00:00
|
|
|
|
2019-05-22 23:22:14 +00:00
|
|
|
(defn plan
|
2019-03-31 03:36:53 +00:00
|
|
|
"General SQL execution function.
|
|
|
|
|
|
2019-04-02 04:31:38 +00:00
|
|
|
Returns a reducible that, when reduced, runs the SQL and yields the result.
|
|
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
Can be called on a `PreparedStatement`, a `Connection`, or something that can
|
|
|
|
|
produce a `Connection` via a `DataSource`."
|
2019-05-29 20:51:11 +00:00
|
|
|
(^clojure.lang.IReduceInit
|
|
|
|
|
[stmt]
|
|
|
|
|
(p/-execute stmt [] {}))
|
|
|
|
|
(^clojure.lang.IReduceInit
|
|
|
|
|
[connectable sql-params]
|
|
|
|
|
(p/-execute connectable sql-params
|
|
|
|
|
{:next.jdbc/sql-params sql-params}))
|
|
|
|
|
(^clojure.lang.IReduceInit
|
|
|
|
|
[connectable sql-params opts]
|
|
|
|
|
(p/-execute connectable sql-params
|
|
|
|
|
(assoc opts :next.jdbc/sql-params sql-params))))
|
2019-01-26 08:21:03 +00:00
|
|
|
|
2019-03-31 06:13:01 +00:00
|
|
|
(defn execute!
|
2019-04-01 06:17:12 +00:00
|
|
|
"General SQL execution function.
|
|
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
Returns a fully-realized result set.
|
2019-04-02 04:31:38 +00:00
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
Can be called on a `PreparedStatement`, a `Connection`, or something that can
|
|
|
|
|
produce a `Connection` via a `DataSource`."
|
2019-03-31 23:54:34 +00:00
|
|
|
([stmt]
|
2019-04-18 06:34:31 +00:00
|
|
|
(p/-execute-all stmt [] {}))
|
2019-03-31 23:54:34 +00:00
|
|
|
([connectable sql-params]
|
2019-04-24 17:07:52 +00:00
|
|
|
(p/-execute-all connectable sql-params
|
2019-04-26 17:34:26 +00:00
|
|
|
{:next.jdbc/sql-params sql-params}))
|
2019-03-31 06:13:01 +00:00
|
|
|
([connectable sql-params opts]
|
2019-04-24 17:07:52 +00:00
|
|
|
(p/-execute-all connectable sql-params
|
2019-04-26 17:34:26 +00:00
|
|
|
(assoc opts :next.jdbc/sql-params sql-params))))
|
2019-01-26 08:21:03 +00:00
|
|
|
|
2019-03-31 06:13:01 +00:00
|
|
|
(defn execute-one!
|
2019-04-01 06:17:12 +00:00
|
|
|
"General SQL execution function that returns just the first row of a result.
|
|
|
|
|
|
2019-04-21 23:13:52 +00:00
|
|
|
Can be called on a `PreparedStatement`, a `Connection`, or something that can
|
|
|
|
|
produce a `Connection` via a `DataSource`."
|
2019-03-31 23:54:34 +00:00
|
|
|
([stmt]
|
2019-04-18 06:34:31 +00:00
|
|
|
(p/-execute-one stmt [] {}))
|
2019-03-31 23:54:34 +00:00
|
|
|
([connectable sql-params]
|
2019-04-24 17:07:52 +00:00
|
|
|
(p/-execute-one connectable sql-params
|
2019-04-26 17:34:26 +00:00
|
|
|
{:next.jdbc/sql-params sql-params}))
|
2019-03-31 06:13:01 +00:00
|
|
|
([connectable sql-params opts]
|
2019-04-24 17:07:52 +00:00
|
|
|
(p/-execute-one connectable sql-params
|
2019-04-26 17:34:26 +00:00
|
|
|
(assoc opts :next.jdbc/sql-params sql-params))))
|
2019-03-31 23:54:34 +00:00
|
|
|
|
2019-04-02 06:43:10 +00:00
|
|
|
(defn transact
|
2019-06-02 16:03:14 +00:00
|
|
|
"Given a transactable object and a function (taking a `Connection`),
|
|
|
|
|
execute the function over the connection in a transactional manner.
|
2019-04-02 06:43:10 +00:00
|
|
|
|
2019-04-18 20:19:59 +00:00
|
|
|
See `with-transaction` for supported options."
|
2019-06-02 16:03:14 +00:00
|
|
|
([transactable f]
|
|
|
|
|
(p/-transact transactable f {}))
|
|
|
|
|
([transactable f opts]
|
|
|
|
|
(p/-transact transactable f opts)))
|
2019-04-02 06:43:10 +00:00
|
|
|
|
2019-03-31 23:54:34 +00:00
|
|
|
(defmacro with-transaction
|
2019-06-02 16:03:14 +00:00
|
|
|
"Given a transactable object, gets a connection and binds it to `sym`,
|
2019-04-21 23:13:52 +00:00
|
|
|
then executes the `body` in that context, committing any changes if the body
|
2019-04-01 06:17:12 +00:00
|
|
|
completes successfully, otherwise rolling back any changes made.
|
|
|
|
|
|
|
|
|
|
The options map supports:
|
2019-04-21 23:13:52 +00:00
|
|
|
* `:isolation` -- `:none`, `:read-committed`, `:read-uncommitted`,
|
|
|
|
|
`:repeatable-read`, `:serializable`,
|
|
|
|
|
* `:read-only` -- `true` / `false`,
|
|
|
|
|
* `:rollback-only` -- `true` / `false`."
|
2019-06-02 16:03:14 +00:00
|
|
|
[[sym transactable opts] & body]
|
2019-07-02 23:45:48 +00:00
|
|
|
(let [con (vary-meta sym assoc :tag 'java.sql.Connection)]
|
|
|
|
|
`(transact ~transactable (^{:once true} fn* [~con] ~@body) (or ~opts {}))))
|