Example of an implicit SessionManager implementation
/* | |
* Mock version of a Database Session | |
*/ | |
class Session { | |
println("opened session") | |
def close() = println("closed session") | |
} | |
/* | |
* Mimic Slick's `database.withSession` method | |
*/ | |
object Database { | |
def withSession[T](f: Session => T) = { | |
val s = new Session | |
try { f(s) } finally { s.close() } | |
} | |
} | |
/* | |
* SessionManager trait, implementations, and companion | |
*/ | |
trait SessionManager extends Any { | |
def withSession[T](f: Session => T): T | |
} | |
class ExistingSessionManager(val s: Session) extends AnyVal with SessionManager { | |
def withSession[T](f: Session => T) = f(s) | |
} | |
object GlobalSessionManager extends SessionManager { | |
def withSession[T](f: Session => T) = Database.withSession(f) | |
} | |
object SessionManager { | |
implicit def getSessionManager(implicit s: Session = null): SessionManager = { | |
if (s == null) GlobalSessionManager else new ExistingSessionManager(s) | |
} | |
} | |
case class User(name: String) | |
trait AccessLayer { | |
def getUser(name: String)(implicit sm: SessionManager): Option[User] | |
} | |
object DatabaseAccessLayer extends AccessLayer { | |
def getUser(name: String)(implicit sm: SessionManager): Option[User] = sm.withSession { implicit session => | |
// pretend actual database queries happen here | |
Some(User(name)) | |
} | |
} | |
class CachingAccessLayer(delegate: AccessLayer) extends AccessLayer { | |
val cache = collection.mutable.Map.empty[String, User] | |
def getUser(name: String)(implicit sm: SessionManager): Option[User] = { | |
// don't worry about thread safety, it's only an example! | |
cache.get(name) orElse { | |
val result = delegate getUser name | |
for (user <- result) cache(name) = user | |
result | |
} | |
} | |
} | |
class LoggingAccessLayer(delegate: AccessLayer) extends AccessLayer { | |
def getUser(name: String)(implicit sm: SessionManager): Option[User] = { | |
println(s"calling getUser($name)") | |
delegate getUser name | |
} | |
} | |
/* Output: | |
first lookup | |
calling getUser(dylemma) | |
opened session | |
closed session | |
second lookup | |
calling getUser(dylemma) | |
group lookup | |
opened session | |
calling getUser(alice) | |
calling getUser(bob) | |
calling getUser(carlos) | |
closed session | |
*/ | |
object ExampleMain extends App { | |
val accessLayer = new LoggingAccessLayer(new CachingAccessLayer(DatabaseAccessLayer)) | |
// cache miss: database opens a session | |
println("first lookup") | |
accessLayer.getUser("dylemma") | |
println | |
// cache hit: no session | |
println("second lookup") | |
accessLayer.getUser("dylemma") | |
println | |
// one session reused for each access | |
println("group lookup") | |
Database.withSession { implicit session => | |
accessLayer.getUser("alice") | |
accessLayer.getUser("bob") | |
accessLayer.getUser("carlos") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment