Skip to content

Commit

Permalink
Implement all JDBC methods for reified objects [IMMUTANT-590]
Browse files Browse the repository at this point in the history
Producing these dynamically should protect us from JDBC version
differences. We do it in a macro to avoid the cost of reflection.
  • Loading branch information
jcrossley3 committed Oct 30, 2015
1 parent ca6ad28 commit defe54d
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 49 deletions.
43 changes: 14 additions & 29 deletions transactions/src/immutant/transactions/jdbc.clj
Expand Up @@ -18,30 +18,25 @@
(:require [immutant.transactions :refer (set-rollback-only)]
[clojure.java.jdbc :as jdbc]))

(defmacro ^:private override-delegate
[type delegate & body]
(let [overrides (group-by first body)
methods (for [m (.getMethods (resolve type))
:let [f (with-meta (symbol (.getName m)) {:tag (-> m .getReturnType .getName)})]
:when (not (overrides f))
:let [args (for [t (.getParameterTypes m)] (with-meta (gensym) {:tag (.getName t)}))]]
(list f (vec (conj args '_)) `(. ~delegate ~f ~@(map #(with-meta % nil) args))))]
`(reify ~type ~@body ~@methods)))

(defn ^:no-doc prepared-statement
[con ^PreparedStatement stmt]
(reify PreparedStatement
(override-delegate PreparedStatement stmt
;; Make the wrapper the statement's back-reference
(getConnection [_] con)

;; Delegate everything else
(executeBatch [_] (.executeBatch stmt))
(executeUpdate [_] (.executeUpdate stmt))
(executeQuery [_] (.executeQuery stmt))
(getUpdateCount [_] (.getUpdateCount stmt))
(getParameterMetaData [_] (.getParameterMetaData stmt))
(getGeneratedKeys [_] (.getGeneratedKeys stmt))
(setFetchSize [_ s] (.setFetchSize stmt s))
(setMaxRows [_ m] (.setMaxRows stmt m))
(setNull [_ x y] (.setNull stmt x y))
(setObject [_ x y] (.setObject stmt x y))
(addBatch [_ b] (.addBatch stmt b))
(addBatch [_] (.addBatch stmt))
(close [_] (.close stmt))))
(getConnection [_] con)))

(defn ^:no-doc connection
[^Connection con]
(reify Connection
(override-delegate Connection con
;; Eat these since they're illegal on an XA connection
(setAutoCommit [& _])
(commit [_])
Expand All @@ -55,17 +50,7 @@
(^PreparedStatement prepareStatement [this ^String a ^int b ^int c]
(prepared-statement this (.prepareStatement con a b c)))
(^PreparedStatement prepareStatement [this ^String a ^int b ^int c ^int d]
(prepared-statement this (.prepareStatement con a b c d)))

;; Delegate everything else
(close [_] (.close con))
(getAutoCommit [_] (.getAutoCommit con))
(createStatement [_] (.createStatement con))
(getMetaData [_] (.getMetaData con))
(getTransactionIsolation [_] (.getTransactionIsolation con))
(setTransactionIsolation [_ v] (.setTransactionIsolation con v))
(isReadOnly [_] (.isReadOnly con))
(setReadOnly [_ v] (.setReadOnly con v))))
(prepared-statement this (.prepareStatement con a b c d)))))

(defn factory
"May be passed via the :factory option to a `clojure.java.jdbc` spec
Expand Down
49 changes: 29 additions & 20 deletions transactions/test/immutant/transactions/jdbc_test.clj
Expand Up @@ -21,28 +21,37 @@

(set-log-level! (or (System/getenv "LOG_LEVEL") :OFF))

(def spec {:factory factory :name "java:jboss/datasources/ExampleDS"})

(if (in-container?)
(deftest jdbc-transactions
(sql/db-do-commands spec
(sql/create-table-ddl :things
[:name :varchar]))

(transaction
(sql/insert! spec :things {:name "success"}))

(transaction
(sql/with-db-transaction [con spec]
(sql/delete! con :things [true])
(sql/db-set-rollback-only! con)))
(let [spec {:factory factory :name "java:jboss/datasources/ExampleDS"}]
(deftest jdbc-transactions
(sql/db-do-commands spec
(sql/create-table-ddl :things
[:name :varchar]))

(try
(transaction
(sql/delete! spec :things [true])
(throw (NegativeArraySizeException. "test rollback by exception")))
(catch NegativeArraySizeException _))
(sql/insert! spec :things {:name "success"}))

(is (= "success" (-> (sql/query spec ["select name from things"])
first
:name)))))
(transaction
(sql/with-db-transaction [con spec]
(sql/delete! con :things [true])
(sql/db-set-rollback-only! con)))

(try
(transaction
(sql/delete! spec :things [true])
(throw (NegativeArraySizeException. "test rollback by exception")))
(catch NegativeArraySizeException _))

(is (= "success" (-> (sql/query spec ["select name from things"])
first
:name)))))

(deftest factory-delegation
(with-open [c (sql/get-connection {:factory factory
:connection-uri "jdbc:h2:mem:ooc"})
s (.prepareStatement c "")]
(.clearParameters s)
(is (not (.isClosed c)))
(.close c)
(is (.isClosed c)))))

0 comments on commit defe54d

Please sign in to comment.