This commit is contained in:
Sean Corfield 2022-11-04 22:17:04 -07:00
parent 06f9b7908c
commit dacf1b3904
3 changed files with 62 additions and 2 deletions

View file

@ -6,6 +6,7 @@ Only accretive/fixative changes will be made from now on.
* Fix [#232](https://github.com/seancorfield/next-jdbc/issues/232) by using `as-cols` in `insert-multi!` SQL builder. Thanks to @changsu-farmmorning for spotting that bug! * Fix [#232](https://github.com/seancorfield/next-jdbc/issues/232) by using `as-cols` in `insert-multi!` SQL builder. Thanks to @changsu-farmmorning for spotting that bug!
* Fix [#229](https://github.com/seancorfield/next-jdbc/issues/229) by adding `next.jdbc.connect/uri->db-spec` which converts a URI string to a db-spec hash map; in addition, if `DriverManager/getConnection` fails, it assumes it was passed a URI instead of a JDBC URL, and retries after calling that function and then recreating the JDBC URL (which should have the effect of moving the embedded user/password credentials into the properties structure instead of the URL). * Fix [#229](https://github.com/seancorfield/next-jdbc/issues/229) by adding `next.jdbc.connect/uri->db-spec` which converts a URI string to a db-spec hash map; in addition, if `DriverManager/getConnection` fails, it assumes it was passed a URI instead of a JDBC URL, and retries after calling that function and then recreating the JDBC URL (which should have the effect of moving the embedded user/password credentials into the properties structure instead of the URL).
* Address [#228](https://github.com/seancorfield/next-jdbc/issues/228) by adding `PreparedStatement` caveat to the Oracle **Tips & Tricks** section. * Address [#228](https://github.com/seancorfield/next-jdbc/issues/228) by adding `PreparedStatement` caveat to the Oracle **Tips & Tricks** section.
* Address [#226](https://github.com/seancorfield/next-jdbc/issues/226) by adding a section on exception handling to **Tips & Tricks** (TL;DR: it's all horribly vendor-specific!).
* Add `on-connection` to exported `clj-kondo` configuration. * Add `on-connection` to exported `clj-kondo` configuration.
* Switch `run-test` from `sh` to `bb`. * Switch `run-test` from `sh` to `bb`.

View file

@ -38,6 +38,42 @@ Consult the [java.sql.Blob documentation](https://docs.oracle.com/javase/8/docs/
> Note: the standard MySQL JDBC driver seems to return `BLOB` data as `byte[]` instead of `java.sql.Blob`. > Note: the standard MySQL JDBC driver seems to return `BLOB` data as `byte[]` instead of `java.sql.Blob`.
## Exceptions
A lot of JDBC operations can fail with an exception. JDBC 4.0 has a
[well-defined hierarchy of exception types](https://docs.oracle.com/en/java/javase/17/docs/api/java.sql/java/sql/package-tree.html)
and you can often catch a specific type of exception to do useful handling
of various error conditions that you might "expect" when working with a
database.
A good example is [SQLIntegrityConstraintViolationException](https://docs.oracle.com/en/java/javase/17/docs/api/java.sql/java/sql/SQLIntegrityConstraintViolationException.html)
which typically represents an index/key constraint violation such as a
duplicate primary key insertion attempt.
However, like some other areas when dealing with JDBC, the reality can
be very database-specific. Some database drivers **don't** use the hierarchy
above -- notably PostgreSQL, which has a generic `PSQLException` type
with its own subclasses and semantics. See [PostgreSQL JDBC issue #963](https://github.com/pgjdbc/pgjdbc/issues/963)
for a discussion of the difficulty in adopting the standard JDBC hierarchy
(dating back five years).
The `java.sql.SQLException` class provides `.getErrorCode()` and
`.getSQLState()` methods but the values returned by those are
explicitly vendor-specific (error code) or only partly standardized (state).
In theory, the SQL state should follow either the X/Open (Open Group) or
ANSI SQL 2003 conventions, both of which were behind paywalls(!). The most
complete public listing is probably the IBM DB2
[SQL State](https://www.ibm.com/docs/en/db2woc?topic=messages-sqlstate)
document.
See also this [Stack Overflow post about SQL State](https://stackoverflow.com/questions/1399574/what-are-all-the-possible-values-for-sqlexception-getsqlstate)
for more references and links. Not all database drivers follow either of
these conventions for SQL State so you may still have to consult your
vendor's specific documentation.
All of this makes writing _generic_ error handling, that works across
multiple databases, very hard indeed. You can't rely on the JDBC `SQLException`
hierarchy; you can sometimes rely on a subset of SQL State values.
## Handling Timeouts ## Handling Timeouts
JDBC provides a number of ways in which you can decide how long an operation should run before it times out. Some of these timeouts are specified in seconds and some are in milliseconds. Some are handled via connection properties (or JDBC URL parameters), some are handled via methods on various JDBC objects. JDBC provides a number of ways in which you can decide how long an operation should run before it times out. Some of these timeouts are specified in seconds and some are in milliseconds. Some are handled via connection properties (or JDBC URL parameters), some are handled via methods on various JDBC objects.

View file

@ -440,6 +440,29 @@ VALUES ('Pear', 'green', 49, 47)
(is (= 4 (count (jdbc/execute! con ["select * from fruit"])))) (is (= 4 (count (jdbc/execute! con ["select * from fruit"]))))
(is (= ac (.getAutoCommit con)))))))))) (is (= ac (.getAutoCommit con))))))))))
#_
(deftest duplicate-insert-test
;; this is primarily a look at exception types/information for #226
(try
(jdbc/execute! (ds) ["
INSERT INTO fruit (id, name, appearance, cost, grade)
VALUES (1234, '1234', '1234', 1234, 1234)
"])
(try
(jdbc/execute! (ds) ["
INSERT INTO fruit (id, name, appearance, cost, grade)
VALUES (1234, '1234', '1234', 1234, 1234)
"])
(println (:dbtype (db)) "allowed duplicate insert")
(catch java.sql.SQLException t
(println (:dbtype (db)) "duplicate insert threw" (type t)
"error" (.getErrorCode t) "state" (.getSQLState t)
"\n\t" (ex-message t))))
(catch java.sql.SQLException t
(println (:dbtype (db)) "will not allow specific ID" (type t)
"error" (.getErrorCode t) "state" (.getSQLState t)
"\n\t" (ex-message t)))))
(deftest bool-tests (deftest bool-tests
(doseq [[n b] [["zero" 0] ["one" 1] ["false" false] ["true" true]] (doseq [[n b] [["zero" 0] ["one" 1] ["false" false] ["true" true]]
:let [v-bit (if (number? b) b (if b 1 0)) :let [v-bit (if (number? b) b (if b 1 0))