Permalink
Browse files

sql: add support for rollback-only to transaction, only wrap the oute…

…rmost transaction's exception with an exception with message 'transaction rolled back'
  • Loading branch information...
1 parent 426e706 commit a5776b8d9e426b87d4e5b941f7f4b195dc85eb08 @scgilardi scgilardi committed Jan 18, 2009
Showing with 59 additions and 12 deletions.
  1. +7 −4 src/clojure/contrib/sql.clj
  2. +25 −7 src/clojure/contrib/sql/internal.clj
  3. +27 −1 src/clojure/contrib/sql/test.clj
@@ -16,12 +16,11 @@
;; Created 2 April 2008
(ns clojure.contrib.sql
- (:use [clojure.contrib.def :only (defvar)])
+ (:use [clojure.contrib.def :only (defalias)])
(:use clojure.contrib.sql.internal))
-(defvar connection connection*
- "Returns the current database connection (or throws if there is none)")
-
+(defalias connection connection*)
+
(defmacro with-connection
"Evaluates body in the context of a new connection to a database then
closes the connection. db-spec is a map containing string values for
@@ -42,6 +41,10 @@
[& body]
`(transaction* (fn [] ~@body)))
+(defalias set-rollback-only set-rollback-only*)
+
+(defalias is-rollback-only is-rollback-only*)
+
(defn do-commands
"Executes SQL commands on the open database connection."
[& commands]
@@ -33,11 +33,23 @@
p))
(defn connection*
- "Returns the current database connection or throws"
+ "Returns the current database connection (or throws if there is none)"
[]
(or (:connection *db*)
(throw (Exception. "no current database connection"))))
+(defn set-rollback-only*
+ "Marks the current stack of nested transactions such that they will
+ rollback rather than commit when complete"
+ []
+ (update-in *db* [:rollback-only] swap! (fn [_] true)))
+
+(defn is-rollback-only*
+ "Returns true if the current stack of nested transactions will rollback
+ rather than commit when complete"
+ []
+ @(:rollback-only *db*))
+
(defn with-connection*
"Evaluates func in the context of a new connection to a database then
closes the connection. db-spec is a map containing string values for
@@ -54,7 +66,8 @@
(java.sql.DriverManager/getConnection
(format "jdbc:%s:%s" subprotocol subname)
(properties (dissoc db-spec :classname :subprotocol :subname)))]
- (binding [*db* (assoc *db* :connection con :level 0)]
+ (binding [*db* (assoc *db* :connection con :level 0
+ :rollback-only (atom false))]
(func))))
(defn transaction*
@@ -73,13 +86,18 @@
(try
(let [value (func)]
(when outermost
- (.commit con))
+ (if (is-rollback-only*)
+ (.rollback con)
+ (.commit con)))
value)
(catch Exception e
- (.rollback con)
- (throw (Exception.
- (format "transaction rolled back: %s"
- (.getMessage e)) e)))
+ (if outermost
+ (do
+ (.rollback con)
+ (throw (Exception.
+ (format "transaction rolled back: %s"
+ (.getMessage e)) e)))
+ (throw e)))
(finally
(when outermost
(.setAutoCommit con auto-commit))))))))
@@ -149,7 +149,7 @@
(.getTables nil nil nil (into-array ["TABLE" "VIEW"])))))))
(defn db-exception
- "Demonstrate rolling back a partially completed transaction"
+ "Demonstrate rolling back a partially completed transaction on exception"
[]
(sql/with-connection
db
@@ -163,3 +163,29 @@
;; is not. the exception will cause it to roll back leaving the database
;; untouched.
(throw (Exception. "sql/test exception")))))
+
+(defn db-rollback
+ "Demonstrate a rollback-only trasaction"
+ []
+ (sql/with-connection
+ db
+ (sql/transaction
+ (prn "is-rollback-only" (sql/is-rollback-only))
+ (sql/set-rollback-only)
+ (sql/insert-values
+ :fruit
+ [:name :appearance]
+ ["Grape" "yummy"]
+ ["Pear" "bruised"])
+ (prn "is-rollback-only" (sql/is-rollback-only))
+ (sql/with-query-results
+ res
+ ["SELECT * FROM fruit"]
+ (doseq [rec res]
+ (println rec))))
+ (prn)
+ (sql/with-query-results
+ res
+ ["SELECT * FROM fruit"]
+ (doseq [rec res]
+ (println rec)))))

0 comments on commit a5776b8

Please sign in to comment.