使用 clojure.java.jdbc/db-do-prepared 检查 uuid 是否存在于 PostgreSQL table 中失败

Checking if uuid exists in PostgreSQL table using clojure.java.jdbc/db-do-prepared fails

我正在连接到 PostgreSQL 数据库,我想知道某个 table 中的 UUID 类型列中是否存在 uuid。参数 uuid 是在另一个函数中生成的。 在 clojure 中,我尝试

(jdbc/db-do-prepared {:datasource datasource}
             "SELECT exists (SELECT 1 FROM account WHERE guid=?::uuid)"
             [uuid])

但是抛出异常:

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)

如果我连接到数据库并粘贴生成的 SQL 并执行它,它工作正常。我还可以使用以下代码从 clojure 插入:

(jdbc/db-do-prepared {:datasource datasource}
                   "INSERT INTO table(guid) VALUES (?::uuid)"
                   [uuid])

来自project.clj的相关依赖项:

[org.clojure/java.jdbc "0.4.2"]
[org.postgresql/postgresql "9.4-1205-jdbc42"]
[hikari-cp "1.3.1"]

来自 do-prepared 的文档:

Return a seq of update counts (one count for each param-group)

UPDATEDELETE 是 return 更新计数的唯一 SQL CRUD 操作。所以 do-prepared 应该只与那些一起使用。但是,还有更直接的抽象:对于 UPDATE 使用 update!,对于 DELETE 使用 delete!.

INSERTs return 生成的键,并且可以使用 do-prepared returning 插入计数。但是,这种方式无法获取生成的主键的值。使用 do-prepared-return-keys 或更好的直接抽象 insert!

SELECTs return 结果集,不是更新计数,不适用于 do-prepared

如果您确实想将批处理 SELECTs 与准备好的语句一起使用,请通过以下方式进行:

  • 使用 with-db-connection 绑定单个数据库连接
  • 在该连接上创建准备好的语句并将其绑定到 var
  • 使用同时引用绑定连接和准备语句的查询函数

像这样:

(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"]))))

虽然我最初认为数据库可以通过参数化优化查询,但进一步的测试表明这可以将查询速度提高 60%。这种加速大部分是在数据库方面,因为语句准备开销可以忽略不计。此测试是使用与提到的 OP 相同的用例完成的,检查 UUID(从 1K 到 100K)。

使用准备好的语句的通用版本可以用一些宏魔术清理成以下内容:

(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"])))