Skip to content

Instantly share code, notes, and snippets.

@Klasu
Last active October 14, 2016 07:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Klasu/a243a9f866d1733bd1e3 to your computer and use it in GitHub Desktop.
Save Klasu/a243a9f866d1733bd1e3 to your computer and use it in GitHub Desktop.
Monad for Database Transactions
// Simple monad to deal with multiple database transactions with same connection, their commits and rollbacks
// Something similar done in http://advorkovyy.blogspot.com.au/2010/10/transactional-monad-for-scala.html
import java.sql.Connection
trait DbTransaction[A] {
def unit: Connection => A
def map[B](f: A => B): DbTransaction[B] = DbTransaction {
connection => f(unit(connection))
}
def flatMap[B](f: A => DbTransaction[B]): DbTransaction[B] = DbTransaction {
connection => f(unit(connection)).unit(connection)
}
import scala.util.{Try, Success, Failure}
def commitBlocking(connection: Connection): Try[A] = Try {
connection.setAutoCommit(false)
val a = unit(connection)
connection.commit()
a
}
import scala.concurrent.{Future, ExecutionContext}
def commit(connection: Connection)(implicit ec: ExecutionContext): Future[A] = Future {
connection.setAutoCommit(false)
val a = unit(connection)
connection.commit()
a
}
}
object DbTransaction {
def apply[A](f: Connection => A): DbTransaction[A] = new DbTransaction[A] {
def unit = f
}
}
// Usage for example with Play Framework and Anorm
object DatabaseOperation {
import anorm._
import play.api.db.DB
import play.api.Play.current
def store(first: String, second: String) = {
val op = for {
f <- DbTransaction {
implicit connection => SQL("INSERT INTO first (value) values ({value})").on('value -> first).execute()
}
g <- DbTransaction {
implicit connection => SQL("INSERT INTO second (value) values ({value})").on('value -> second).execute()
}
} yield g
// This DB.withConnection can also be moved within commit/commitBlocking of DbTransaction trait
// but then it would be bound to the DB implementation. This way it is more generic.
DB.withConnection { implicit conn => op.commitBlocking(conn) }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment