or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.typesafe.slick/slick_2.12@2.1.x

docs

database-drivers.mdindex.mdjdbc-integration.mdlifted-embedding.mdprofiles.mdschema-modeling.mdtable-definitions.md
tile.json

tessl/maven-com-typesafe-slick--slick_2-12

tessl install tessl/maven-com-typesafe-slick--slick_2-12@2.1.0

Scala Language-Integrated Connection Kit - A modern database query and access library for Scala that allows you to work with stored data almost as if you were using Scala collections while at the same time giving you full control over when a database access happens and which data is transferred.

lifted-embedding.mddocs/

Lifted Embedding DSL

The Lifted Embedding DSL is Slick's main query language that provides type-safe database queries using a collection-like API. It "lifts" Scala expressions to database queries, allowing you to write queries that look like Scala collection operations but compile to efficient SQL.

Core Query Types

Query Base Types

The foundation of Slick's query system:

// Base trait for all query representations
sealed trait QueryBase[T] extends Rep[T]

// Main query class
sealed abstract class Query[+E, U, C[_]] extends QueryBase[C[U]] {
  def shaped: ShapedValue[_ <: E, U]
}

// Base representation trait
trait Rep[T]

Query Class

The main Query class provides all query operations:

sealed abstract class Query[+E, U, C[_]] extends QueryBase[C[U]] {
  // Monadic operations
  def map[F, G, T](f: E => F)(implicit shape: Shape[_ <: FlatShapeLevel, F, T, G]): Query[G, T, C]
  def flatMap[F, T, D[_]](f: E => Query[F, T, D]): Query[F, T, C]
  
  // Filtering
  def filter[T <: Column[_]](f: E => T)(implicit wt: CanBeQueryCondition[T]): Query[E, U, C]
  def withFilter[T <: Column[_]](f: E => T)(implicit wt: CanBeQueryCondition[T]): Query[E, U, C]
  def filterNot[T <: Column[_]](f: E => T)(implicit wt: CanBeQueryCondition[T]): Query[E, U, C]
  
  // Sorting
  def sortBy[T <% Ordered](f: E => T): Query[E, U, C]
  
  // Grouping  
  def groupBy[K, T, G, P](f: E => K)(implicit kshape: Shape[_ <: FlatShapeLevel, K, T, G]): Query[G, P, C]
  
  // Joins
  def join[E2, U2, D[_]](q2: Query[E2, U2, D]): Query[(E, E2), (U, U2), C]
  def leftJoin[E2, U2, D[_]](q2: Query[E2, U2, D]): Query[(E, Option[E2]), (U, Option[U2]), C]
  def rightJoin[E2, U2, D[_]](q2: Query[E2, U2, D]): Query[(Option[E], E2), (Option[U], U2), C]
  def outerJoin[E2, U2, D[_]](q2: Query[E2, U2, D]): Query[(Option[E], Option[E2]), (Option[U], Option[U2]), C]
  def zip[E2, U2, D[_]](q2: Query[E2, U2, D]): Query[(E, E2), (U, U2), C]
  def zipWith[E2, U2, F, G, T, D[_]](q2: Query[E2, U2, D], f: (E, E2) => F): Query[G, T, C]
  def zipWithIndex: Query[(E, Column[Long]), (U, Long), C]
  
  // Pagination
  def take(num: Long): Query[E, U, C]
  def drop(num: Long): Query[E, U, C]
  
  // Aggregation
  def length: Column[Int]
  def countDistinct: Column[Int]
  def exists: Column[Boolean]
  
  // Set operations
  def union[O >: E, R, D[_]](other: Query[O, R, D]): Query[O, R, C]
  def unionAll[O >: E, R, D[_]](other: Query[O, R, D]): Query[O, R, C]
  def ++[O >: E, R, D[_]](other: Query[O, R, D]): Query[O, R, C]
  
  // Execution (when imported from driver)
  def list()(implicit session: Session): List[U]
  def first()(implicit session: Session): U  
  def firstOption()(implicit session: Session): Option[U]
  def run()(implicit session: Session): C[U]
}

TableQuery

Specialized query type for table operations:

class TableQuery[E <: AbstractTable[_]] extends Query[E, E#TableElementType, Seq] {
  // Insert operations
  def +=(value: E#TableElementType)(implicit session: Session): Unit
  def ++=(values: Iterable[E#TableElementType])(implicit session: Session): Unit
  def insert(value: E#TableElementType)(implicit session: Session): Int
  def insertAll(values: E#TableElementType*)(implicit session: Session): Option[Int]
  
  // Update operations
  def update(value: E#TableElementType)(implicit session: Session): Int
  
  // Delete operations  
  def delete()(implicit session: Session): Int
  
  // DDL operations
  def ddl: DDL
}

// Factory object
object TableQuery {
  def apply[E <: AbstractTable[_]](cons: Tag => E): TableQuery[E]
  def apply[E <: AbstractTable[_]](implicit tt: TypeTag[E]): TableQuery[E] = macro TableQueryMacro.apply[E]
}

Column System

Column Types

// Base column representation
abstract class Column[T] extends Rep[T] {
  // Ordering
  def asc: ColumnOrdered[T]
  def desc: ColumnOrdered[T]
  
  // Null operations
  def isNull: Column[Boolean]
  def isNotNull: Column[Boolean]
  def ifNull[S >: T](replacement: S): Column[S]
  
  // Optional conversion
  def ? : Column[Option[T]]
}

// Constant columns (client-side values)  
abstract class ConstColumn[T] extends Column[T]

// Literal values known at compile-time
class LiteralColumn[T](val value: T) extends ConstColumn[T]

// Runtime parameters
class ParameterColumn[T] extends ConstColumn[T]

// Ordered columns for sorting
sealed trait ColumnOrdered[+T]

Column Extension Methods

Type-safe operations on columns:

// Base extension framework
trait ExtensionMethods[B1, P1] {
  protected implicit def c1: Column[P1]
}

// Core column operations
final class ColumnExtensionMethods[B1, P1](val c: Column[P1]) extends ExtensionMethods[B1, P1] {
  // Equality operations (type-safe)
  def ===[P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]): Column[R]
  def =!=[P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]): Column[R]
  
  // Comparison operations
  def <[P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]): Column[R]
  def <=[P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]): Column[R]
  def >[P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]): Column[R]
  def >=[P2, R](e: Column[P2])(implicit om: o#arg[B1, P2]#to[Boolean, R]): Column[R]
  
  // Set membership
  def inSet[R](values: Traversable[B1]): Column[Boolean]
  def inSetBind[R](values: Traversable[B1]): Column[Boolean]
  def between[P2, P3, R](start: Column[P2], end: Column[P3]): Column[Boolean]
}

// String-specific operations
final class StringColumnExtensionMethods[P1](val c: Column[P1]) extends ExtensionMethods[String, P1] {
  def like[R](e: Column[String]): Column[Boolean]
  def ++[P2, R](e: Column[P2]): Column[String]  // String concatenation
  def startsWith[R](s: String): Column[Boolean]
  def endsWith[R](s: String): Column[Boolean]
  def toUpperCase: Column[String]  
  def toLowerCase: Column[String]
  def ltrim: Column[String]
  def rtrim: Column[String]
  def trim: Column[String]
}

// Numeric operations  
final class NumericColumnExtensionMethods[B1, P1](val c: Column[P1]) extends ExtensionMethods[B1, P1] {
  def +[P2, R](e: Column[P2]): Column[R] 
  def -[P2, R](e: Column[P2]): Column[R]
  def *[P2, R](e: Column[P2]): Column[R]
  def /[P2, R](e: Column[P2]): Column[R]
  def %[P2, R](e: Column[P2]): Column[R]
  def abs: Column[B1]
  def ceil: Column[B1]
  def floor: Column[B1]
  def sign: Column[Int]
}

// Boolean operations
final class BooleanColumnExtensionMethods[P1](val c: Column[P1]) extends ExtensionMethods[Boolean, P1] {
  def &&[P2, R](b: Column[P2]): Column[Boolean]
  def ||[P2, R](b: Column[P2]): Column[Boolean]  
  def unary_! : Column[Boolean]
}

Query Construction Examples

Basic Filtering and Mapping

import scala.slick.driver.H2Driver.simple._

// Table definition
class Users(tag: Tag) extends Table[(Int, String, Option[String])](tag, "users") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def email = column[Option[String]]("email")
  def * = (id, name, email)
}

val users = TableQuery[Users]

// Basic queries
val query1 = users.filter(_.name === "John")
val query2 = users.map(_.email)
val query3 = users.filter(_.id > 10).map(u => (u.name, u.email))

// Chaining operations
val activeUsers = users
  .filter(_.email.isDefined)
  .sortBy(_.name.asc)
  .take(50)

Joins

class Orders(tag: Tag) extends Table[(Int, Int, String)](tag, "orders") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def userId = column[Int]("user_id")
  def product = column[String]("product")
  def * = (id, userId, product)
}

val orders = TableQuery[Orders]

// Inner join
val userOrders = users.join(orders).on(_.id === _.userId)
val userOrdersQuery = userOrders.map { case (u, o) => (u.name, o.product) }

// Left join  
val allUsersOrders = users.joinLeft(orders).on(_.id === _.userId)
val leftJoinQuery = allUsersOrders.map { case (u, o) => (u.name, o.map(_.product)) }

// Complex join with filtering
val recentOrders = users
  .join(orders).on(_.id === _.userId)
  .filter { case (u, o) => u.email.isDefined && o.product.like("%laptop%") }
  .map { case (u, o) => (u.name, o.product) }

Aggregation and Grouping

// Count operations
val userCount = users.length
val usersWithEmail = users.filter(_.email.isDefined).length

// Grouping
val ordersByUser = orders
  .groupBy(_.userId)
  .map { case (userId, orders) => (userId, orders.length) }

// Complex aggregation
class OrderItems(tag: Tag) extends Table[(Int, Int, Double)](tag, "order_items") {
  def orderId = column[Int]("order_id")
  def quantity = column[Int]("quantity")  
  def price = column[Double]("price")
  def * = (orderId, quantity, price)
}

val orderItems = TableQuery[OrderItems]

val orderTotals = orderItems
  .groupBy(_.orderId)
  .map { case (orderId, items) => 
    (orderId, items.map(i => i.quantity * i.price).sum)
  }

Advanced Query Patterns

// Subqueries  
val richUsers = users.filter(_.id in orders.filter(_.product === "luxury").map(_.userId))

// Exists  
val usersWithOrders = users.filter(u => orders.filter(_.userId === u.id).exists)

// Union
val activeUsers = users.filter(_.email.isDefined)
val vipUsers = users.filter(_.name.startsWith("VIP"))
val combinedUsers = activeUsers.union(vipUsers)

// Case expressions
val userType = users.map { u =>
  Case
    .when(u.email.isDefined).then("active")
    .when(u.name.startsWith("VIP")).then("vip") 
    .otherwise("regular")
}

// Window functions (if supported by database)
val rankedUsers = users
  .map(u => (u.name, Functions.rowNumber.over.sortBy(u.id.desc)))
  .sortBy(_._2.asc)

Query Conditions

Type-safe query conditions using the CanBeQueryCondition typeclass:

// Typeclass for valid query conditions
trait CanBeQueryCondition[-T] {
  def apply(value: T): Column[Boolean]
}

// Standard instances
implicit val columnCanBeQueryCondition: CanBeQueryCondition[Column[Boolean]]
implicit val booleanCanBeQueryCondition: CanBeQueryCondition[Boolean]
implicit def optionCanBeQueryCondition[T](implicit ev: CanBeQueryCondition[T]): CanBeQueryCondition[Option[T]]

Usage in queries:

// Valid conditions
users.filter(_.email.isDefined)  // Column[Boolean]
users.filter(_ => true)          // Boolean literal
users.filter(_.name === "John")  // Column comparison

// Combining conditions
users.filter(u => u.email.isDefined && u.name.startsWith("J"))
users.filter(u => u.id > 10 || u.name === "Admin")

Shape System

The shape system handles type mapping between query expressions and result types:

// Core shape abstraction
abstract class Shape[Level, -Mixed_, Unpacked_, Packed_] {
  def pack(from: Mixed_): Packed_  
  def unpack(from: Packed_): Mixed_
  def packedShape: Shape[Level, Packed_, Unpacked_, Packed_]
  def toNode(value: Mixed_): Node
}

// Proven shape for table projections
type ProvenShape[T] = Shape[FlatShapeLevel, T, T, Column[T]]

// Shape levels
sealed trait ShapeLevel
trait FlatShapeLevel extends ShapeLevel
trait NestedShapeLevel extends ShapeLevel

// Shaped values  
case class ShapedValue[T, U](value: T, shape: Shape[_ <: ShapeLevel, T, U, _]) {
  def toNode: Node = shape.toNode(value)
}

Common shape patterns:

// Single column
val nameShape: ProvenShape[String] = users.map(_.name).shaped

// Tuple shapes
val userTuple: ProvenShape[(Int, String)] = users.map(u => (u.id, u.name)).shaped

// Case class mapping (with macro support)
case class User(id: Int, name: String, email: Option[String])

val userProjection = users.map(u => (u.id, u.name, u.email)) <> (User.tupled, User.unapply)

Pre-compiled Queries

Optimize frequently-used parameterized queries:

// Compiled query wrapper
class Compiled[T] {
  def apply(param: T#Param): T#Result
}

// Compilation functions
object Compiled {
  def apply[V, R](f: V => R): Compiled[V => R]
}

Usage examples:

// Simple parameter
val userById = Compiled { id: Column[Int] =>
  users.filter(_.id === id)
}

// Multiple parameters  
val userByNameAndEmail = Compiled { (name: Column[String], email: Column[String]) =>
  users.filter(u => u.name === name && u.email === email)
}

// Usage
Database.forURL("...") withSession { implicit session =>
  val user1 = userById(1).first
  val user2 = userByNameAndEmail("John", "john@example.com").firstOption
}

Function Calls

Custom SQL function calls:

// Simple function calls
object SimpleFunction {
  def apply[R](name: String): SimpleBinaryOperator[R]
  def apply[T1, R](name: String): SimpleFunction[T1, R]
  def apply[T1, T2, R](name: String): SimpleFunction[(T1, T2), R]
  // ... up to 22 parameters
}

// Binary operators
object SimpleBinaryOperator {
  def apply[R](name: String): SimpleBinaryOperator[R]
}

Usage examples:

// Custom functions
val upper = SimpleFunction.unary[String]("upper")
val concat = SimpleFunction.binary[String]("concat")
val random = SimpleFunction.nullary[Double]("random")

// Usage in queries  
val upperNames = users.map(u => upper(u.name))
val fullNames = users.map(u => concat(u.name, " Suffix"))
val randomScores = users.map(u => (u.name, random()))

Type Mapping

Custom type mapping for user-defined types:

// Base trait for custom mappings
trait MappedTo[T] extends Any {
  def value: T
}

// Column type construction
trait ColumnType[T] extends TypedType[T]

Usage examples:

// Status enum mapping
sealed trait Status extends MappedTo[String]
case object Active extends Status { val value = "active" }
case object Inactive extends Status { val value = "inactive" }

implicit val statusColumnType = MappedColumnType.base[Status, String](
  _.value, 
  {
    case "active" => Active
    case "inactive" => Inactive  
  }
)

// Usage in tables
class Users(tag: Tag) extends Table[(Int, String, Status)](tag, "users") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def status = column[Status]("status")  // Uses custom mapping
  def * = (id, name, status)
}