I agree that wrapping the functions is a sensible approach. Using wrap-transaction is precisely how I ended up doing it with the conman yesql wrapper https://github.com/luminus-framework/conman
The approach I took there is to have the generated functions use the connection atom, and have with-transaction rebind it to the transactional connection within its scope. However, the functions also accept an explicit connection, and with-transaction also provides explicit access to the transactional connection: (with-transaction [t-conn conn] (jdbc/db-set-rollback-only! t-conn) (create-user! {:id "foo" :first_name "Sam" :last_name "Smith" :email "sam.sm...@example.com"}) (get-user {:id "foo"})) This approach works for testing, since the connection can be passed explicitly, but I would argue that in practice it's better to use a separate test database instead of the dev instance. On Wednesday, August 5, 2015 at 11:11:19 AM UTC-4, James Reeves wrote: > > On 5 August 2015 at 14:03, Dmitri <dmitri....@gmail.com <javascript:>> > wrote: > >> What I'm talking about is whether it's a better pattern to leave a >> repetitive and error prone task to the user or encapsulate it in a single >> place. The whole discussion boils down to following options. >> >> The first option is that we keep functions pure and the connection is >> passed as a parameter by the person writing the code (the user). With this >> approach the burden is squarely on the user to remember to pass the correct >> connection to the function. This becomes error prone for things like >> transactions where the developer has to pass the transaction connection as >> opposed to the normal database connection. The worst part in this scenario >> is that the code will run except it won't run transactionally. This is a >> bug that is difficult to catch as it only surfaces in cases where the >> transaction should be rolled back. >> > > It's worth pointing out that you don't need to use dynamic or global vars > to avoid this scenario. You could just remove the original database > connection from scope: > > (defn foobar* [tx] > (foo tx) > (bar tx)) > > (defn foobar [db] > (sql/with-transaction [tx db] (foobar* tx)) > > Or shadow the original binding: > > (defn foobar [db] > (sql/with-transaction [db db] > (foo db) > (bar db))) > > Ideally you also want a way of testing this behaviour, even with dynamic > or global scope. If it's critical to your application that database > operations run in a transaction, you should have a way of verifying that. > > For instance, you might use a protocol to factor out the operations on the > database: > > (defprotocol Database > (wrap-transaction [db f]) > (foo db) > (bar db)) > > (defn foobar [db] > (wrap-transaction db > (fn [tx] > (foo tx) > (bar tx)))) > > This allows tests to be written to verify that foo and bar are called > within wrap-transaction, and to verify our production implementation of the > protocol correctly wraps the function f in a SQL transaction. > > If you're writing something that depends upon behaviour that can't be > verified, you're going to run into problems no matter how you structure > your application. > > >> The alternative is to encapsulate the database connection management in >> the initialization logic in the namespace managing the connection. This way >> the query functions can be context aware and ensure that the correct >> connection is used automatically. >> > > Do you mean storing the database connection in a global var, not just a > dynamically scoped one? > > In such a case, how do you run tests without wiping the development > database? Do you run the tests in a separate process, or shut down the dev > server before running tests, or do you not mind if your development > database is cleared by your tests? > > - James > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.