Skip to content

Instantly share code, notes, and snippets.

@mielientiev
Last active February 5, 2019 21:20
Show Gist options
  • Save mielientiev/3b2b37a0fe4db15f93df7ccd9cf39213 to your computer and use it in GitHub Desktop.
Save mielientiev/3b2b37a0fe4db15f93df7ccd9cf39213 to your computer and use it in GitHub Desktop.
Slick Final Tagless approach
// build.sbt
val slick = Seq(
"com.typesafe.slick" %% "slick" % "3.2.1",
"org.slf4j" % "slf4j-nop" % "1.6.4",
"com.typesafe.slick" %% "slick-hikaricp" % "3.2.1",
"com.h2database" % "h2" % "1.4.196"
)
val scalaz = Seq(
"org.scalaz" %% "scalaz-core" % "7.2.17"
)
libraryDependencies ++= slick ++ scalaz
----------
// resources/application.conf
h2mem = {
url = "jdbc:h2:mem:test1"
driver = org.h2.Driver
connectionPool = disabled
keepAliveConnection = true
}
----------
//Product.scala
case class Product(id: Option[Int], name: String)
----------
//ProductDao.scala
trait ProductDao[F[_]] {
def create: F[Unit]
def findByName(name: String): F[Option[Product]]
def save(product: Product): F[Product]
}
----------
//SlickProductDao.scala
import slick.dbio._
import slick.jdbc.JdbcProfile
class SlickProductDao(val jdbcProfile: JdbcProfile) extends ProductDao[DBIO] {
import jdbcProfile.api._
class ProductTable(tag: Tag) extends Table[Product](tag, "Product") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def name = column[String]("NAME")
override def * = (id.?, name) <> (Product.tupled, Product.unapply)
}
val productTable = TableQuery[ProductTable]
override def findByName(name: String): DBIO[Option[Product]] = productTable.filter(_.name === name).result.headOption
override def save(product: Product): DBIO[Product] = (productTable returning productTable.map(_.id)
into ((savedProduct,id) => savedProduct.copy(id=Some(id)))
) += product
override def create: DBIO[Unit] = productTable.schema.create
}
----------
//ProductService.scala
trait ProductService[F[_]] {
def findByNameOrSave(product: Product): F[Product]
}
----------
//DefaultProductService.scala
import scala.concurrent.ExecutionContext
import scalaz.{Monad, OptionT, ~>}
class DefaultProductService[F[_]: Monad, DB[_]: Monad](productDao: ProductDao[DB], execute: DB ~> F) extends ProductService[F] {
override def findByNameOrSave(product: Product): F[Product] = {
val result: DB[Product] =
OptionT(productDao.findByName(product.name))
.getOrElseF(productDao.save(product))
execute.apply(result)
}
}
-----------
// dbio.scala
trait DBIOInstances {
implicit def instance(implicit ec: ExecutionContext): Monad[DBIO] = new Monad[DBIO] {
override def bind[A, B](fa: DBIO[A])(f: A => DBIO[B]) = fa.flatMap(f)
override def point[A](a: => A): DBIO[A] = DBIO.successful(a)
}
}
object dbio extends DBIOInstances
---------
// Main.scala
import slick.dbio._
import slick.jdbc.{H2Profile, JdbcBackend, JdbcProfile}
import slick.jdbc.JdbcBackend.Database
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, ExecutionContext, Future}
import scalaz._
import scalaz.Scalaz._
import dbio._
object Main {
def main(args: Array[String]): Unit = {
implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
val h2db: JdbcBackend.Database = Database.forConfig("h2mem")
val productDao = new SlickProductDao(H2Profile)
val dBIOTransformer = dBIOTransformation(H2Profile, h2db)
val service = new DefaultProductService[Future, DBIO](productDao, dBIOTransformer)
Await.result(dBIOTransformer(productDao.create), Duration.Inf)
println(Await.result(service.findByNameOrSave(Product(None, "abc")), Duration.Inf))
println(Await.result(service.findByNameOrSave(Product(None, "abc")), Duration.Inf))
}
private def dBIOTransformation(profile: JdbcProfile, db: JdbcBackend.Database): DBIO ~> Future = new (DBIO ~> Future) {
import profile.api._
override def apply[A](fa: DBIO[A]): Future[A] = db.run(fa.transactionally)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment