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.

jdbc-integration.mddocs/

JDBC Integration and Plain SQL

Slick's JDBC integration provides low-level database access and plain SQL query support. It includes type-safe parameter binding, result extraction, and direct SQL execution capabilities alongside the lifted embedding DSL.

JDBC Backend

Database and Session Management

// JDBC Backend implementation
object JdbcBackend extends DatabaseComponent {
  type Database = DatabaseDef
  type Session = SessionDef
  
  // Database definition
  abstract class DatabaseDef {
    def withSession[T](f: Session => T): T
    def withTransaction[T](f: Session => T): T
    def withDynSession[T](f: => T): T
    def withDynTransaction[T](f: => T): T
    def createSession(): Session
    def close(): Unit
  }
  
  // Session definition  
  trait SessionDef extends Closeable {
    def conn: java.sql.Connection
    def metaData: java.sql.DatabaseMetaData
    def capabilities: Set[Capability]
    def withPreparedStatement[T](sql: String)(f: java.sql.PreparedStatement => T): T
    def withStatement[T](f: java.sql.Statement => T): T
    
    // Transaction management
    def rollback(): Unit
    def commit(): Unit
    def isAutoCommit: Boolean
    def setAutoCommit(autoCommit: Boolean): Unit
  }
}

// Database factory methods
object Database {
  def forURL(url: String, driver: String = null, user: String = null, password: String = null, 
             executor: AsyncExecutor = null): Database
  def forDataSource(ds: javax.sql.DataSource, executor: AsyncExecutor = null): Database  
  def forConfig(path: String, config: Config = null, executor: AsyncExecutor = null): Database
  def forDriver(driver: java.sql.Driver, url: String, user: String = null, password: String = null,
                executor: AsyncExecutor = null): Database
  def forName(name: String, executor: AsyncExecutor = null): Database  // JNDI lookup
}

Basic Database Operations

import scala.slick.driver.H2Driver.simple._
import scala.slick.jdbc.JdbcBackend.Database

val database = Database.forURL("jdbc:h2:mem:test1", driver="org.h2.Driver")

// Session management
database withSession { implicit session =>
  // Database operations within session
  users.ddl.create
  users += (1, "John")
}

// Transaction management  
database withTransaction { implicit session =>
  // All operations in transaction
  users += (1, "John")
  orders += (1, 1, "Product")  
  // Automatic rollback on exception
}

// Manual session management
val session = database.createSession() 
try {
  users.ddl.create()(session)
  users += (1, "John")(session)
} finally {
  session.close()
}

Type System Integration

JDBC Type Mapping

// Core JDBC type mapping
trait JdbcType[T] extends TypedType[T] {
  def sqlType: Int
  def sqlTypeName(sym: Option[FieldSymbol]): String
  def getValue(r: ResultSet, idx: Int): T
  def setValue(v: T, p: PreparedStatement, idx: Int): Unit
  def updateValue(v: T, r: ResultSet, idx: Int): Unit
  def hasLiteralForm: Boolean
  def valueToSQLLiteral(value: T): String
}

// Standard type mappings (available implicitly)
implicit val booleanJdbcType: JdbcType[Boolean]
implicit val byteJdbcType: JdbcType[Byte]  
implicit val shortJdbcType: JdbcType[Short]
implicit val intJdbcType: JdbcType[Int]
implicit val longJdbcType: JdbcType[Long]
implicit val floatJdbcType: JdbcType[Float]
implicit val doubleJdbcType: JdbcType[Double]
implicit val stringJdbcType: JdbcType[String]
implicit val bigDecimalJdbcType: JdbcType[BigDecimal]
implicit val dateJdbcType: JdbcType[java.sql.Date]
implicit val timestampJdbcType: JdbcType[java.sql.Timestamp] 
implicit val blobJdbcType: JdbcType[java.sql.Blob]
implicit val clobJdbcType: JdbcType[java.sql.Clob]

// Optional type mappings
implicit def optionJdbcType[T](implicit base: JdbcType[T]): JdbcType[Option[T]]

Custom Type Mappings

// Mapped column type factory
object MappedColumnType {
  def base[T, U](to: T => U, from: U => T)(implicit tm: JdbcType[U]): JdbcType[T]
}

Custom type examples:

import java.util.UUID

// UUID mapping to String
implicit val uuidColumnType = MappedColumnType.base[UUID, String](
  uuid => uuid.toString,
  str => UUID.fromString(str)
)

// Enum mapping
sealed trait Status
case object Active extends Status
case object Inactive extends Status
case object Pending extends Status  

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

// JSON mapping (requires JSON library)
import play.api.libs.json._

implicit val jsonColumnType = MappedColumnType.base[JsValue, String](
  json => Json.stringify(json),
  str => Json.parse(str)
)

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

Plain SQL Queries

StaticQuery Interface

// Static SQL query execution
class StaticQuery[P, R](query: String, pconv: SetParameter[P], rconv: GetResult[R]) extends Invoker[R] {
  def apply(param: P): StaticQueryInvoker[R]
  def list(param: P)(implicit session: Session): List[R]
  def first(param: P)(implicit session: Session): R
  def firstOption(param: P)(implicit session: Session): Option[R]
  def execute(param: P)(implicit session: Session): Int
}

// Factory methods
object StaticQuery {
  def apply[R](query: String)(implicit rconv: GetResult[R]): StaticQuery[Unit, R]
  def apply[P, R](query: String)(implicit pconv: SetParameter[P], rconv: GetResult[R]): StaticQuery[P, R]
  
  // Update queries
  def update[P](query: String)(implicit pconv: SetParameter[P]): StaticQuery[P, Int]
  def updateNA(query: String): StaticQuery[Unit, Int]
}

SQL String Interpolation

// SQL interpolation support
implicit class SQLInterpolation(val sc: StringContext) extends AnyVal {
  def sql[R](param: Any*)(implicit rconv: GetResult[R]): StaticQuery[Unit, R]
  def sqlu(param: Any*): StaticQuery[Unit, Int] // Update queries
}

Basic SQL query examples:

import scala.slick.driver.H2Driver.simple._
import scala.slick.jdbc.{StaticQuery => Q, GetResult}

// Simple query without parameters
val allUsers = sql"SELECT id, name FROM users".as[(Int, String)]

Database.forURL("...") withSession { implicit session =>
  val users: List[(Int, String)] = allUsers.list
}

// Parameterized queries  
def userById(id: Int) = sql"SELECT id, name, email FROM users WHERE id = $id".as[(Int, String, Option[String])]
def usersByStatus(status: String) = sql"SELECT * FROM users WHERE status = $status".as[(Int, String, String)]

Database.forURL("...") withSession { implicit session =>
  val user: Option[(Int, String, Option[String])] = userById(1).firstOption
  val activeUsers: List[(Int, String, String)] = usersByStatus("active").list
}

// Update queries
def updateUserName(id: Int, newName: String) = sqlu"UPDATE users SET name = $newName WHERE id = $id"
def deleteUser(id: Int) = sqlu"DELETE FROM users WHERE id = $id" 

Database.forURL("...") withSession { implicit session =>
  val rowsUpdated = updateUserName(1, "John Doe").execute
  val rowsDeleted = deleteUser(2).execute
}

Result Extraction

// Result extraction trait
trait GetResult[T] {
  def apply(rs: PositionedResult): T
}

// Standard GetResult instances
implicit val GetBoolean: GetResult[Boolean]
implicit val GetByte: GetResult[Byte]
implicit val GetInt: GetResult[Int]
implicit val GetLong: GetResult[Long] 
implicit val GetFloat: GetResult[Float]
implicit val GetDouble: GetResult[Double]
implicit val GetString: GetResult[String]
implicit val GetDate: GetResult[java.sql.Date]
implicit val GetTimestamp: GetResult[java.sql.Timestamp]
implicit val GetBigDecimal: GetResult[BigDecimal]

// Option support
implicit def GetOption[T](implicit base: GetResult[T]): GetResult[Option[T]]

// Tuple support (up to 22 elements)
implicit def GetTuple2[T1, T2](implicit g1: GetResult[T1], g2: GetResult[T2]): GetResult[(T1, T2)]
// ... up to GetTuple22

Positioned Result Navigation

// ResultSet navigation with position tracking
class PositionedResult(val rs: ResultSet) {
  var pos = 0
  
  // Basic value extraction
  def nextBoolean(): Boolean
  def nextByte(): Byte
  def nextShort(): Short  
  def nextInt(): Int
  def nextLong(): Long
  def nextFloat(): Float
  def nextDouble(): Double
  def nextString(): String
  def nextDate(): java.sql.Date
  def nextTimestamp(): java.sql.Timestamp
  def nextBigDecimal(): BigDecimal
  def nextBlob(): java.sql.Blob
  def nextClob(): java.sql.Clob
  
  // Optional value extraction
  def nextBooleanOption(): Option[Boolean]
  def nextIntOption(): Option[Int]
  def nextStringOption(): Option[String]
  // ... other option methods
  
  // Generic extraction
  def nextObject(): AnyRef
  def skip(): Unit
  def restart(): Unit
  
  // Utility methods
  def <<[T](implicit f: GetResult[T]): T = f(this)
}

Custom GetResult examples:

// Case class result extraction
case class User(id: Int, name: String, email: Option[String], createdAt: java.sql.Timestamp)

implicit val getUserResult = GetResult(r => 
  User(r.nextInt(), r.nextString(), r.nextStringOption(), r.nextTimestamp())
)

// Alternative using <<  operator
implicit val getUserResult2 = GetResult(r => 
  User(r.<<, r.<<, r.<<, r.<<)
)

// Usage
val query = sql"SELECT id, name, email, created_at FROM users"
Database.forURL("...") withSession { implicit session =>
  val users: List[User] = query.as[User].list
}

// Complex extraction with computed fields
case class UserSummary(id: Int, displayName: String, hasEmail: Boolean)

implicit val getUserSummaryResult = GetResult { r =>
  val id = r.nextInt()
  val firstName = r.nextString() 
  val lastName = r.nextStringOption()
  val email = r.nextStringOption()
  
  UserSummary(
    id = id,
    displayName = firstName + lastName.map(" " + _).getOrElse(""),
    hasEmail = email.isDefined
  )
}

val summaryQuery = sql"SELECT id, first_name, last_name, email FROM users"
val summaries: List[UserSummary] = summaryQuery.as[UserSummary].list

Parameter Setting

// Parameter setting trait
trait SetParameter[T] {
  def apply(value: T, params: PositionedParameters): Unit
}

// Standard SetParameter instances  
implicit val SetBoolean: SetParameter[Boolean]
implicit val SetInt: SetParameter[Int] 
implicit val SetLong: SetParameter[Long]
implicit val SetString: SetParameter[String]
implicit val SetDate: SetParameter[java.sql.Date]
implicit val SetTimestamp: SetParameter[java.sql.Timestamp]
// ... other standard types

// Option support
implicit def SetOption[T](implicit base: SetParameter[T]): SetParameter[Option[T]]

// Tuple support
implicit def SetTuple2[T1, T2](implicit s1: SetParameter[T1], s2: SetParameter[T2]): SetParameter[(T1, T2)]
// ... up to SetTuple22

Positioned Parameters

// Parameter setting with position tracking
class PositionedParameters(val ps: PreparedStatement) {
  var pos = 0
  
  // Basic parameter setting
  def setBoolean(value: Boolean): Unit
  def setInt(value: Int): Unit
  def setLong(value: Long): Unit 
  def setString(value: String): Unit
  def setDate(value: java.sql.Date): Unit
  def setTimestamp(value: java.sql.Timestamp): Unit
  def setBigDecimal(value: BigDecimal): Unit
  def setNull(sqlType: Int): Unit
  
  // Generic parameter setting
  def setObject(value: AnyRef): Unit
  def skip(): Unit
  def restart(): Unit
  
  // Utility method
  def >>[T](value: T)(implicit f: SetParameter[T]): Unit = f(value, this)
}

Custom SetParameter examples:

// Custom parameter for enum
implicit val setUserStatus = SetParameter[Status] { (value, params) =>
  params.setString(value match {
    case Active => "active"
    case Inactive => "inactive" 
    case Pending => "pending"
  })
}

// Custom parameter for case class
case class DateRange(start: java.sql.Date, end: java.sql.Date)

implicit val setDateRange = SetParameter[DateRange] { (range, params) =>
  params.setDate(range.start)
  params.setDate(range.end)
}

// Usage in queries
def usersByStatusAndDate(status: Status, dateRange: DateRange) = 
  sql"""SELECT * FROM users 
        WHERE status = $status 
        AND created_at BETWEEN ${dateRange.start} AND ${dateRange.end}""".as[User]

Advanced SQL Features

Batch Operations

// Batch insert with plain SQL
def insertUsersBatch(users: List[User]) = {
  val insertSQL = "INSERT INTO users (name, email, status) VALUES (?, ?, ?)"
  
  Database.forURL("...") withSession { implicit session =>
    session.withPreparedStatement(insertSQL) { ps =>
      users.foreach { user =>
        ps.setString(1, user.name)
        ps.setString(2, user.email.orNull)
        ps.setString(3, user.status.toString.toLowerCase)
        ps.addBatch()
      }
      ps.executeBatch()
    }
  }
}

// Batch update
def updateUserStatusBatch(updates: List[(Int, Status)]) = {
  val updateSQL = "UPDATE users SET status = ? WHERE id = ?"
  
  Database.forURL("...") withSession { implicit session =>
    session.withPreparedStatement(updateSQL) { ps =>
      updates.foreach { case (id, status) =>
        ps.setString(1, status.toString.toLowerCase)
        ps.setInt(2, id)
        ps.addBatch()
      }
      ps.executeBatch()
    }
  }
}

Streaming Results

import scala.slick.util.CloseableIterator

// Stream large result sets
def streamAllUsers(): CloseableIterator[User] = {
  val query = sql"SELECT id, name, email, created_at FROM users ORDER BY id"
  
  Database.forURL("...") withSession { implicit session =>
    query.as[User].iterator // Returns CloseableIterator
  }
}

// Usage
val userIterator = streamAllUsers()
try {
  userIterator.foreach { user =>
    // Process user without loading all into memory
    processUser(user)
  }
} finally {
  userIterator.close()
}

Dynamic Query Building

// Build queries dynamically with plain SQL
def searchUsers(nameFilter: Option[String], statusFilter: Option[Status], limit: Int): List[User] = {
  val conditions = List(
    nameFilter.map(name => s"name LIKE '%$name%'"),
    statusFilter.map(status => s"status = '${status.toString.toLowerCase}'")
  ).flatten
  
  val whereClause = if (conditions.nonEmpty) {
    "WHERE " + conditions.mkString(" AND ")
  } else ""
  
  val query = s"""
    SELECT id, name, email, created_at 
    FROM users 
    $whereClause 
    ORDER BY name 
    LIMIT $limit
  """
  
  Database.forURL("...") withSession { implicit session =>
    StaticQuery.queryNA[User](query).list
  }
}

Stored Procedures

// Call stored procedures
def callStoredProcedure(userId: Int): List[Order] = {
  Database.forURL("...") withSession { implicit session =>
    session.withPreparedStatement("CALL get_user_orders(?)") { ps =>
      ps.setInt(1, userId)
      val rs = ps.executeQuery()
      val result = new scala.collection.mutable.ListBuffer[Order]
      
      while (rs.next()) {
        result += Order(
          id = rs.getInt("id"),
          userId = rs.getInt("user_id"), 
          product = rs.getString("product"),
          quantity = rs.getInt("quantity")
        )
      }
      result.toList
    }
  }
}

// Function calls  
def getUserCount(): Int = {
  Database.forURL("...") withSession { implicit session =>
    sql"SELECT count_users()".as[Int].first
  }
}

Connection Configuration

// Advanced connection configuration
val database = Database.forURL(
  url = "jdbc:postgresql://localhost:5432/mydb",
  user = "username", 
  password = "password",
  driver = "org.postgresql.Driver"
)

// Using connection pool
import com.zaxxer.hikari.HikariDataSource

val dataSource = new HikariDataSource()
dataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb")
dataSource.setUsername("username")
dataSource.setPassword("password") 
dataSource.setMaximumPoolSize(20)

val database = Database.forDataSource(dataSource)

// Configuration file approach
// application.conf:
// mydb {
//   url = "jdbc:postgresql://localhost:5432/mydb"
//   driver = "org.postgresql.Driver"  
//   user = "username"
//   password = "password"
//   connectionPool = "HikariCP"
//   maximumPoolSize = 20
// }

val database = Database.forConfig("mydb")