expand docs/tests for #206

This commit is contained in:
Sean Corfield 2022-05-20 11:53:53 -07:00
parent 0bdffbc87f
commit 3ed1f4b99c
5 changed files with 75 additions and 11 deletions

View file

@ -2,6 +2,10 @@
Only accretive/fixative changes will be made from now on. Only accretive/fixative changes will be made from now on.
* 1.2.next in progress
* Enhance `insert-multi!` to accept a sequence of hash maps and also to support batch execution, via PR [#206](https://github.com/seancorfield/next-jdbc/pull/206) -- [@rschmukler](https://github.com/rschmukler).
* Fix HikariCP pooling example.
* 1.2.780 -- 2022-04-04 * 1.2.780 -- 2022-04-04
* Address [#204](https://github.com/seancorfield/next-jdbc/issues/204) by adding `next.jdbc/on-connection`. * Address [#204](https://github.com/seancorfield/next-jdbc/issues/204) by adding `next.jdbc/on-connection`.
* Address [#203](https://github.com/seancorfield/next-jdbc/issues/203) by adding a note to the **PostgreSQL Tips & Tricks** section. * Address [#203](https://github.com/seancorfield/next-jdbc/issues/203) by adding a note to the **PostgreSQL Tips & Tricks** section.

View file

@ -53,6 +53,20 @@ Given a table name (as a keyword), a vector of column names, and a vector of row
"Aunt Sally" "sour@lagunitas.beer"] {:return-keys true}) "Aunt Sally" "sour@lagunitas.beer"] {:return-keys true})
``` ```
Given a table name (as a keyword) and a vector of hash maps, this performs a multi-row insertion into the database:
```clojure
(sql/insert-multi! ds :address
[{:name "Stella", :email "stella@artois.beer"}
{:name "Waldo", :email "waldo@lagunitas.beer"}
{:name "Aunt Sally", :email "sour@lagunitas.beer"}])
;; equivalent to
(jdbc/execute! ds ["INSERT INTO address (name,email) VALUES (?,?), (?,?), (?,?)"
"Stella" "stella@artois.beer"
"Waldo" "waldo@lagunitas.beer"
"Aunt Sally" "sour@lagunitas.beer"] {:return-keys true})
```
> Note: this expands to a single SQL statement with placeholders for every > Note: this expands to a single SQL statement with placeholders for every
value being inserted -- for large sets of rows, this may exceed the limits value being inserted -- for large sets of rows, this may exceed the limits
on SQL string size and/or number of parameters for your JDBC driver or your on SQL string size and/or number of parameters for your JDBC driver or your
@ -60,7 +74,42 @@ database. Several databases have a limit of 1,000 parameter placeholders.
Oracle does not support this form of multi-row insert, requiring a different Oracle does not support this form of multi-row insert, requiring a different
syntax altogether. syntax altogether.
You should look at [`next.jdbc/execute-batch!`](https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/api/next.jdbc#execute-batch!) for an alternative approach. ### Batch Insertion
As of release 1.2.next, you can specify `:batch true` in the options, which
will use `execute-batch!` under the hood, instead of `execute!`, as follows:
```clojure
(sql/insert-multi! ds :address
[:name :email]
[["Stella" "stella@artois.beer"]
["Waldo" "waldo@lagunitas.beer"]
["Aunt Sally" "sour@lagunitas.beer"]]
{:batch true})
;; equivalent to
(jdbc/execute-batch! ds
["INSERT INTO address (name,email) VALUES (?,?)"
["Stella" "stella@artois.beer"]
["Waldo" "waldo@lagunitas.beer"]
["Aunt Sally" "sour@lagunitas.beer"]]
{:return-keys true :return-generated-keys true})
;; and
(sql/insert-multi! ds :address
[:name :email]
[{:name "Stella", :email "stella@artois.beer"}
{:name "Waldo", :email "waldo@lagunitas.beer"}
{:name "Aunt Sally", :email "sour@lagunitas.beer"}]
{:batch true})
;; equivalent to
(jdbc/execute-batch! ds
["INSERT INTO address (name,email) VALUES (?,?)"
["Stella" "stella@artois.beer"]
["Waldo" "waldo@lagunitas.beer"]
["Aunt Sally" "sour@lagunitas.beer"]]
{:return-keys true :return-generated-keys true})
```
See [**Batched Parameters**](https://cljdoc.org/d/com.github.seancorfield/next.jdbc/CURRENT/doc/getting-started/prepared-statements#caveats) for caveats and possible database-specific behaviors.
## `query` ## `query`

View file

@ -1,4 +1,4 @@
;; copyright (c) 2019-2021 Sean Corfield, all rights reserved ;; copyright (c) 2019-2022 Sean Corfield, all rights reserved
(ns next.jdbc.sql (ns next.jdbc.sql
"Some utility functions that make common operations easier by "Some utility functions that make common operations easier by
@ -43,15 +43,17 @@
(merge {:return-keys true} opts))))) (merge {:return-keys true} opts)))))
(defn insert-multi! (defn insert-multi!
"Syntactic sugar over `execute!` to make inserting columns/rows easier. "Syntactic sugar over `execute!` or `execute-batch!` to make inserting
columns/rows easier.
Given a connectable object, a table name, a sequence of column names, and Given a connectable object, a table name, a sequence of column names, and
a vector of rows of data (vectors of column values), inserts the data as a vector of rows of data (vectors of column values), inserts the data as
multiple rows in the database and attempts to return a vector of maps of multiple rows in the database and attempts to return a vector of maps of
generated keys. generated keys.
Also supports a sequence of hash maps with keys corresponding to column Given a connectable object, a table name, a sequence of hash maps of data,
names. inserts the data as multiple rows in the database and attempts to return
a vector of maps of generated keys.
If called with `:batch` true will call `execute-batch!` - see its documentation If called with `:batch` true will call `execute-batch!` - see its documentation
for situations in which the generated keys may or may not be returned as well as for situations in which the generated keys may or may not be returned as well as
@ -68,12 +70,15 @@
([connectable table hash-maps] ([connectable table hash-maps]
(insert-multi! connectable table hash-maps {})) (insert-multi! connectable table hash-maps {}))
([connectable table hash-maps-or-cols opts-or-rows] ([connectable table hash-maps-or-cols opts-or-rows]
(if-not (-> hash-maps-or-cols first map?) (if (map? (first hash-maps-or-cols))
(insert-multi! connectable table hash-maps-or-cols opts-or-rows {})
(let [cols (keys (first hash-maps-or-cols)) (let [cols (keys (first hash-maps-or-cols))
->row (fn ->row [m] ->row (fn ->row [m]
(map (partial get m) cols))] (map #(get m %) cols))]
(insert-multi! connectable table cols (map ->row hash-maps-or-cols) opts-or-rows)))) (when-not (apply = (map (comp set keys) hash-maps-or-cols))
(throw (IllegalArgumentException.
"insert-multi! hash maps must all have the same keys")))
(insert-multi! connectable table cols (map ->row hash-maps-or-cols) opts-or-rows))
(insert-multi! connectable table hash-maps-or-cols opts-or-rows {})))
([connectable table cols rows opts] ([connectable table cols rows opts]
(if (seq rows) (if (seq rows)
(let [opts (merge (:options connectable) opts) (let [opts (merge (:options connectable) opts)

View file

@ -1,4 +1,4 @@
;; copyright (c) 2019-2021 Sean Corfield, all rights reserved ;; copyright (c) 2019-2022 Sean Corfield, all rights reserved
(ns next.jdbc.sql.builder (ns next.jdbc.sql.builder
"Some utility functions for building SQL strings. "Some utility functions for building SQL strings.
@ -157,7 +157,9 @@
(into [(str "INSERT INTO " (table-fn (safe-name table)) (into [(str "INSERT INTO " (table-fn (safe-name table))
" (" params ")" " (" params ")"
" VALUES " " VALUES "
(str/join ", " (repeat (if batch? 1 (count rows)) (str "(" places ")"))) (if batch?
(str "(" places ")")
(str/join ", " (repeat (count rows) (str "(" places ")"))))
(when-let [suffix (:suffix opts)] (when-let [suffix (:suffix opts)]
(str " " suffix)))] (str " " suffix)))]
(if batch? identity cat) (if batch? identity cat)

View file

@ -199,6 +199,10 @@
(is (thrown? clojure.lang.ExceptionInfo (is (thrown? clojure.lang.ExceptionInfo
(sql/insert-multi! (ds) :fruit [] [[] [] []])))) (sql/insert-multi! (ds) :fruit [] [[] [] []]))))
(deftest no-mismatched-columns
(is (thrown? IllegalArgumentException
(sql/insert-multi! (ds) :fruit [{:name "Apple"} {:cost 1.23}]))))
(deftest no-empty-order-by (deftest no-empty-order-by
(is (thrown? clojure.lang.ExceptionInfo (is (thrown? clojure.lang.ExceptionInfo
(sql/find-by-keys (ds) :fruit (sql/find-by-keys (ds) :fruit