Reputation: 247
I'm connecting to a PostgreSQL database and I want to know whether or not a uuid exists in a UUID-type column in some table. The parameter uuid is generated in another function. In clojure I try
(jdbc/db-do-prepared {:datasource datasource}
"SELECT exists (SELECT 1 FROM account WHERE guid=?::uuid)"
[uuid])
But it throws an exception:
BatchUpdateException Batch entry 0 SELECT exists (SELECT 1 FROM table WHERE guid='dbe8cda5-d37c-3446-9b9c-85e0344af3b1'::uuid) was aborted. Call getNextException to see the cause. org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError (AbstractJdbc2Statement.java:2781)
If I connect to the database and paste the generated SQL and execute it it works fine. I can also insert from clojure using the following code:
(jdbc/db-do-prepared {:datasource datasource}
"INSERT INTO table(guid) VALUES (?::uuid)"
[uuid])
Relevant dependencies from project.clj:
[org.clojure/java.jdbc "0.4.2"]
[org.postgresql/postgresql "9.4-1205-jdbc42"]
[hikari-cp "1.3.1"]
Upvotes: 3
Views: 935
Reputation: 6956
From the documentation of do-prepared
:
Return a seq of update counts (one count for each param-group)
UPDATE
and DELETE
are the only SQL CRUD actions that return update counts. So do-prepared
should only be used with those. However, there are more direct abstractions: for UPDATE
use update!
and for DELETE
use delete!
.
INSERT
s return generated keys, and can work with do-prepared
returning insert counts. However, this way you cannot get the value of generated primary keys. Use do-prepared-return-keys
, or better, the direct abstraction insert!
SELECT
s return result sets, not an update count, and won't work with do-prepared
.
If you do want to use batch SELECT
s with a prepared statement, do so by:
with-db-connection
Like so:
(j/with-db-connection [c datasource]
(let [ps (j/prepare-statement (j/get-connection c)
"SELECT count(*) from person where left(name,1)=?")]
(doall (map #(j/query c [ps %]) ["a" "b" "c"]))))
While I initially thought the database would be able to optimize queries thanks to their parametrization, further tests have shown me that this can speed up queries by 60%. Most of this speedup is on the database side, since statement preparation overhead is negligible. This test was done with the same use case as the OP mentioned, checking UUIDs (from 1K to 100K).
A generic version for using prepared statements can be cleaned up with a little macro magic into the following:
(defmacro with-prepared-statement
[[connection-bind datasource
statement-bind sql & keys]
& body]
`(clojure.java.jdbc/with-db-connection [~connection-bind datasource]
(let [~statement-bind (apply clojure.java.jdbc/prepare-statement
(clojure.java.jdbc/get-connection datasource) ~sql ~keys)]
~@body)))
(with-prepared-statement [c datasource
ps "SELECT count(*) from persoon where left(voornaam,1)=?"]
(doall (map #(j/query c [ps %]) ["a" "b" "c"])))
Upvotes: 1