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.
* 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
* 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.

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})
```
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
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
@ -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
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`

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
"Some utility functions that make common operations easier by
@ -43,15 +43,17 @@
(merge {:return-keys true} opts)))))
(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
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
generated keys.
Also supports a sequence of hash maps with keys corresponding to column
names.
Given a connectable object, a table name, a sequence of hash maps of data,
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
for situations in which the generated keys may or may not be returned as well as
@ -68,12 +70,15 @@
([connectable table hash-maps]
(insert-multi! connectable table hash-maps {}))
([connectable table hash-maps-or-cols opts-or-rows]
(if-not (-> hash-maps-or-cols first map?)
(insert-multi! connectable table hash-maps-or-cols opts-or-rows {})
(if (map? (first hash-maps-or-cols))
(let [cols (keys (first hash-maps-or-cols))
->row (fn ->row [m]
(map (partial get m) cols))]
(insert-multi! connectable table cols (map ->row hash-maps-or-cols) opts-or-rows))))
(map #(get m %) cols))]
(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]
(if (seq rows)
(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
"Some utility functions for building SQL strings.
@ -157,7 +157,9 @@
(into [(str "INSERT INTO " (table-fn (safe-name table))
" (" params ")"
" 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)]
(str " " suffix)))]
(if batch? identity cat)

View file

@ -199,6 +199,10 @@
(is (thrown? clojure.lang.ExceptionInfo
(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
(is (thrown? clojure.lang.ExceptionInfo
(sql/find-by-keys (ds) :fruit