Last active
August 24, 2017 14:10
-
-
Save davidhoyt/98cbe0a94840373b6e62 to your computer and use it in GitHub Desktop.
Slick 3 and cats
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case class UserId(value: String) extends AnyVal with slick.lifted.MappedTo[String] | |
case class User(id: UserId, userName: String, password: String) | |
trait UsersTable extends TableDefinition with TableSchema { provider: DriverProvider => | |
import driver.api._ | |
override type TableRecordType = User | |
override val table = TableQuery[TableDefinition] | |
class TableDefinition(tag: Tag) extends Table[User](tag, "Users") { | |
def id = column[UserId]("ID", O.PrimaryKey, O.Length(length = 256, varying = true)) | |
def userName = column[String]("UserName", O.Length(length = 256, varying = true)) | |
def password = column[String]("Password", O.Length(length = 256, varying = true)) | |
override def * = (id, userName, password) <> ((User.apply _).tupled, User.unapply) | |
} | |
} | |
trait UsersQuery { provider: DriverProvider with UsersTable => | |
import provider.driver._ | |
import provider.driver.api._ | |
def byId(id: UserId) = table.filter(_.id === id).result | |
def drop(users: Seq[UserId]) = table.filter(_.id inSetBind users).delete | |
} | |
object Users { | |
class Users(val driver: JdbcDriver) | |
extends UsersTable | |
with UsersQuery | |
with DriverProvider | |
trait Provider { | |
val users: Users | |
} | |
def apply(driver: JdbcDriver): Users = | |
new Users(driver) | |
def samples = Seq( | |
User(UserId("M1"), userName = "user1", password = "password1"), | |
User(UserId("M2"), userName = "user2", password = "password2"), | |
User(UserId("M3"), userName = "user3", password = "password3") | |
) | |
} | |
class MyDomainRegistry(path: String, config: Config, val driver: JdbcDriver) | |
extends Users.Provider { | |
import scala.concurrent.Future | |
import driver.api._ | |
override val users = Users(driver) | |
val db = Database.forConfig(path, config) | |
val tables = Seq[TableSchema](users) | |
val createTablesAction = TableSchema.create(tables) | |
val createTablesIfNotExistAction = TableSchema.createIfNotExists(tables)(db.ioExecutionContext) | |
val dropTablesAction = TableSchema.drop(tables) | |
def runCreateTables(): Future[Unit] = db.run(createTablesAction) | |
def runCreateTablesIfNotExist(): Future[Unit] = db.run(createTablesIfNotExistAction) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.concurrent.ExecutionContext | |
import slick.driver.JdbcDriver | |
import slick.jdbc.meta.MTable | |
import slick.profile.SqlProfile | |
trait DriverProvider { | |
val driver: JdbcDriver | |
} | |
trait TableDefinition { provider: DriverProvider => | |
import cats.{Monoid, Foldable} | |
import slick.dbio._ | |
import driver.api.{Table, TableQuery} | |
type TableRecordType | |
type TableDefinition <: Table[TableRecordType] | |
val table: TableQuery[TableDefinition] | |
def batch[R <: TableRecordType, R2, F[_] : Foldable, E <: Effect](entities: F[R])(fn: (TableQuery[TableDefinition], R) => DBIOAction[R2, NoStream, E])(implicit m: Monoid[DBIOAction[R2, NoStream, E]]): DBIOAction[R2, NoStream, E] = | |
Foldable[F].foldMap(entities) { r => | |
fn(table, r) | |
} | |
def insertOrUpdateBatch[R <: TableRecordType, F[_] : Foldable](entities: F[R])(implicit m: Monoid[DBIOAction[Int, NoStream, Effect.Write]]) = | |
batch(entities) { (tbl, entity) => | |
import driver.api._ | |
tbl.insertOrUpdate(entity) | |
} | |
} | |
trait TableSchema { provider: DriverProvider with TableDefinition => | |
import TableSchema.SchemaDbIoAction | |
import slick.dbio.{Effect => GenEffect} | |
import driver._ | |
import driver.api._ | |
lazy val schema: SchemaDescription = table.schema | |
lazy val schemaName: Option[String] = table.baseTableRow.schemaName | |
lazy val tableName: String = table.baseTableRow.tableName | |
def createSchema(): SchemaDbIoAction[GenEffect.Schema] = | |
schema.create | |
def dropSchema(): SchemaDbIoAction[GenEffect.Schema] = | |
schema.drop | |
} | |
object TableSchema { | |
import slick.dbio._ | |
type SchemaDbIoAction[T <: Effect] = DBIOAction[Unit, NoStream, T] | |
type SchemaDescription = SqlProfile#SchemaDescription | |
def dbioActionZero[T <: Effect]: SchemaDbIoAction[T] = | |
DBIO.successful(()).asInstanceOf[SchemaDbIoAction[T]] | |
def create(tableSchemas: TableSchema*): SchemaDbIoAction[Effect.Schema] = | |
create[Seq](tableSchemas) | |
def create[M[T] <: TraversableOnce[T]](tableSchemas: M[TableSchema]): SchemaDbIoAction[Effect.Schema] = | |
tableSchemas.foldLeft(dbioActionZero[Effect.Schema])(_ >> _.createSchema()) | |
def createIfNotExists(tableSchemas: TableSchema*)(implicit executor: ExecutionContext): SchemaDbIoAction[Effect.Read with Effect.Schema] = | |
createIfNotExists[Seq](tableSchemas) | |
def createIfNotExists[M[T] <: TraversableOnce[T]](tableSchemas: M[TableSchema])(implicit executor: ExecutionContext): SchemaDbIoAction[Effect.Read with Effect.Schema] = | |
foldWithMTables(tableSchemas)(_.isEmpty, _.createSchema()) | |
def drop(tableSchemas: TableSchema*): SchemaDbIoAction[Effect.Schema] = | |
drop[Seq](tableSchemas) | |
def drop[M[T] <: TraversableOnce[T]](tableSchemas: M[TableSchema]): SchemaDbIoAction[Effect.Schema] = | |
tableSchemas.foldLeft(dbioActionZero[Effect.Schema])(_ >> _.dropSchema()) | |
def dropIfExists(tableSchemas: TableSchema*)(implicit executor: ExecutionContext): SchemaDbIoAction[Effect.Read with Effect.Schema] = | |
dropIfExists[Seq](tableSchemas) | |
def dropIfExists[M[T] <: TraversableOnce[T]](tableSchemas: M[TableSchema])(implicit executor: ExecutionContext): SchemaDbIoAction[Effect.Read with Effect.Schema] = | |
foldWithMTables(tableSchemas)(_.nonEmpty, _.dropSchema()) | |
def foldWithMTables[M[T] <: TraversableOnce[T]](tableSchemas: M[TableSchema])(predicate: Vector[MTable] => Boolean, fn: TableSchema => SchemaDbIoAction[Effect.Schema])(implicit executor: ExecutionContext): SchemaDbIoAction[Effect.Read with Effect.Schema] = | |
tableSchemas.foldLeft(dbioActionZero[Effect.Read with Effect.Schema]) { | |
case (actions, schema) => | |
actions >> MTable.getTables(schema.tableName).flatMap { tables => | |
//filter() gives a runtime error. | |
//TODO: Reexamine after the next slick 3.x release. | |
if (predicate(tables)) | |
fn(schema) | |
else | |
dbioActionZero[Effect.Schema] | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Not using kind projector | |
import cats._ | |
import slick.dbio._ | |
implicit def dbioActionMonoid[R : Monoid, E <: Effect]: Monoid[DBIOAction[R, NoStream, E]] = | |
new Monoid[DBIOAction[R, NoStream, E]] { | |
override val empty: DBIOAction[R, NoStream, E] = | |
DBIO.successful(Monoid[R].empty) | |
override def combine(x: DBIOAction[R, NoStream, E], y: DBIOAction[R, NoStream, E]): DBIOAction[R, NoStream, E] = | |
x >> y | |
} | |
implicit def dbioActionFunctor[E <: Effect](implicit ec: ExecutionContext): Functor[({type F[A] = DBIOAction[A, NoStream, E]})#F] = | |
new Functor[({type F[A] = DBIOAction[A, NoStream, E]})#F] { | |
override def map[A, B](fa: DBIOAction[A, NoStream, E])(f: A => B): DBIOAction[B, NoStream, E] = | |
fa map f | |
} | |
implicit def dbioActionMonad[E <: Effect](implicit ec: ExecutionContext) = | |
new Monad[({type M[A] = DBIOAction[A, NoStream, E]})#M] { | |
override def pure[A](x: A): DBIOAction[A, NoStream, E] = | |
DBIO.successful(x) | |
override def flatMap[A, B](fa: DBIOAction[A, NoStream, E])(f: A => DBIOAction[B, NoStream, E]): DBIOAction[B, NoStream, E] = | |
fa flatMap f | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On second thought,
DBIOAction
s are not necessarily associative when composed and so theMonoid
implementation should be ignored... 😊