I want to reset an Oracle expired password programmatically, which I can achieve using interop directly against java.sql.DriverManager, by calling DriverManager/getConnection and supplying a Properties "info" argument which contains the Oracle specific connection property "oracle.jdbc.newPassword". Is it possible to do this using next.jdbc?
@mark926 In theory, you can just add :oracle.jdbc.newPassword "newsecret"
to your db-spec hash map and when you first call get-connection
, that will happen.
When next.jdbc
processes a db-spec hash map, it extracts the parts it needs to build the JDBC URL itself and then builds a Properties
object with the rest of the hash map and passes it to the driver when creating that connection.
See https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/connection.clj#L141-L146 — etc
is the “rest” of the db-spec hash map after the URL parts have been removed https://github.com/seancorfield/next-jdbc/blob/develop/src/next/jdbc/connection.clj#L162-L165
Is there a way to conveniently generate a record builder given a record type? There's probably some reflection-heavy way but I'd rather not go there if I can avoid it A way to generalize this https://github.com/seancorfield/next-jdbc/blob/develop/test/next/jdbc/result_set_test.clj#L382
I’d ask: why would you want to do that?
(but there’s always map->MyRecord
available for every record type which you could map
over the result set if you really must have records instead of hash maps)
That worked, thought I'd tried that - but obv hadn't got it quite right - thanks
Don't get me wrong, I don't really want to do that 🙂 It's a temporary measure meant to fix a bad design choice in a service which shouldn't even be using an in-memory sql db. I hope I can improve its performance that way, assuming building records instead of maps will be both faster and less GC intensive.
which means I would ideally like to avoid the map->Record
constructor
Just ended up wrapping defrecord
to emit the constructor
(defn emit-builder
[name fields]
(let [builder (symbol (str "->" name "Builder"))
rs (with-meta 'rs {:tag (.getName ResultSet)})
rsmeta (with-meta 'rsmeta {:tag (.getName ResultSetMetaData)})]
`(defn ~builder [~rs ~rsmeta]
(reify
rs/RowBuilder
(~'->row [_#] (new ~name ~@(for [f (map str fields)] `(.getObject ~rs ~f))))
(~'column-count [_#] 0)
(~'with-column [_# row# _#] row#)
(~'with-column-value [_# row# _# _#] row#)
(~'row! [_# row#] row#)
rs/ResultSetBuilder
(~'->rs [_#] (transient []))
(~'with-row [_# ~'rs row#] (conj! ~rs row#))
(~'rs! [_# ~'rs] (persistent! ~rs))
clojure.lang.ILookup
(~'valAt [this# k#] (get this# k# nil))
(~'valAt [this# k# not-found#]
(case k#
:cols ~(mapv keyword fields)
:rsmeta ~rsmeta
not-found#))))))
(defmacro defsqlrecord
[name fields & opts]
(let [builder (symbol (str "->" name "Builder"))
adapter (symbol (str name "Adapter"))]
`(do
(defrecord ~name ~fields ~@opts)
~(emit-builder name fields)
(def ~adapter (next-adapter/hugsql-adapter-next-jdbc
{:builder-fn ~builder}))
~name)))
Not building a result set is going to be faster — if you can use plan
and reduce
.
true, that will be the next step, but it will require a more involved solution
My commiserations 🙂
I've seen things you would not believe 🙂