or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

database-io.mddatabase-profiles.mdindex.mdplain-sql.mdqueries.mdtable-definitions.mdtype-mappings.md
tile.json

database-profiles.mddocs/

Database Profiles

Database-specific profiles providing connection management, SQL generation, and database-specific features in Slick.

Capabilities

Profile Architecture

Profiles provide database-specific implementations and expose the complete API through their api objects.

/**
 * Base profile trait that all database profiles extend
 */
trait BasicProfile {
  /** The API object containing all profile-specific types and methods */
  val api: API
  
  /** Database backend for this profile */
  type Backend <: BasicBackend
  
  /** Database type for this profile */
  type Database = Backend#Database
}

/**
 * Relational profile extending BasicProfile with table support
 */
trait RelationalProfile extends BasicProfile {
  /** Column type definitions */
  type ColumnType[T] <: TypedType[T]
  
  /** Table query definitions */
  type TableQuery[E <: AbstractTable[_]] <: Query[E, E#TableElementType, Seq]
  
  /** Schema description type */
  type SchemaDescription <: SchemaDescriptionDef
}

/**
 * JDBC profile providing JDBC-specific functionality
 */
trait JdbcProfile extends RelationalProfile {
  /** JDBC backend type */
  type Backend = JdbcBackend
  
  /** JDBC action type */
  type ProfileAction[+R, +S <: NoStream, -E <: Effect] = DBIOAction[R, S, E]
  
  /** Simple JDBC action type */
  type SimpleDBIO[+R] = ProfileAction[R, NoStream, Effect.All]
}

Database-Specific Profiles

Concrete profile implementations for different database systems.

/**
 * H2 database profile
 */
object H2Profile extends JdbcProfile {
  override val api = new API {}
}

/**
 * PostgreSQL database profile  
 */
object PostgresProfile extends JdbcProfile {
  override val api = new API {}
}

/**
 * MySQL database profile
 */
object MySQLProfile extends JdbcProfile {
  override val api = new API {}
}

/**
 * SQL Server database profile
 */
object SQLServerProfile extends JdbcProfile {
  override val api = new API {}
}

/**
 * Oracle database profile
 */
object OracleProfile extends JdbcProfile {
  override val api = new API {}
}

/**
 * SQLite database profile
 */
object SQLiteProfile extends JdbcProfile {
  override val api = new API {}
}

/**
 * Derby database profile
 */
object DerbyProfile extends JdbcProfile {
  override val api = new API {}
}

Usage Examples:

// Import profile-specific API
import slick.jdbc.PostgresProfile.api._

// Or choose profile dynamically
val profile = if (usePostgres) PostgresProfile else H2Profile
import profile.api._

// Profile-specific features
import slick.jdbc.PostgresProfile.api._
val query = sql"SELECT * FROM users WHERE data @> ${jsonValue}".as[User]

Database Connection

Create and manage database connections using various configuration methods.

/**
 * Database connection factory and management
 */
trait Database {
  /** Run a database action and return a Future */
  def run[R](a: DBIOAction[R, NoStream, Nothing]): Future[R]
  
  /** Stream database results using Reactive Streams */
  def stream[T](a: StreamingDBIO[_, T]): DatabasePublisher[T]
  
  /** Close the database connection */
  def close(): Unit
  
  /** Get the database configuration */
  def source: DataSource
}

object Database {
  /**
   * Create database from JDBC URL
   */
  def forURL(url: String, user: String = null, password: String = null, prop: Properties = null, driver: String = null, executor: AsyncExecutor = AsyncExecutor.default()): Database
  
  /**
   * Create database from configuration
   */
  def forConfig(path: String, config: Config = ConfigFactory.load(), driver: String = null, classLoader: ClassLoader = ClassLoaderUtil.defaultClassLoader): Database
  
  /**
   * Create database from DataSource
   */
  def forDataSource(ds: DataSource, maxConnections: Option[Int] = None, executor: AsyncExecutor = AsyncExecutor.default()): Database
  
  /**
   * Create database from JNDI name
   */  
  def forName(jndiName: String, executor: AsyncExecutor = AsyncExecutor.default()): Database
}

Usage Examples:

// Direct JDBC URL
val db = Database.forURL(
  url = "jdbc:postgresql://localhost/test",
  user = "postgres", 
  password = "password",
  driver = "org.postgresql.Driver"
)

// From configuration file (application.conf)
val db = Database.forConfig("mydb")

// Configuration example (application.conf):
/*
mydb {
  url = "jdbc:postgresql://localhost/test"
  driver = "org.postgresql.Driver"
  user = "postgres"
  password = "password"
  connectionPool = HikariCP
  maxConnections = 20
  minConnections = 5
  numThreads = 10
}
*/

// From DataSource
val dataSource: DataSource = // ... obtain DataSource
val db = Database.forDataSource(dataSource, maxConnections = Some(20))

// From JNDI
val db = Database.forName("java:comp/env/jdbc/mydb")

// Custom executor
val customExecutor = AsyncExecutor("custom", numThreads = 5, queueSize = 1000)
val db = Database.forURL("jdbc:h2:mem:test", executor = customExecutor)

Connection Pooling

Configure connection pooling for production applications.

/**
 * Asynchronous executor for database operations
 */
case class AsyncExecutor(
  name: String,
  minThreads: Int,
  maxThreads: Int, 
  queueSize: Int,
  maxConnections: Int = Integer.MAX_VALUE
)

object AsyncExecutor {
  /** Default executor configuration */
  def default(name: String = "AsyncExecutor.default", numThreads: Int = 20): AsyncExecutor
  
  /** Apply configuration from Config */
  def apply(name: String, config: Config): AsyncExecutor
}

Usage Examples:

// Custom connection pool configuration
val executor = AsyncExecutor(
  name = "myapp-db",
  minThreads = 5,
  maxThreads = 20,
  queueSize = 1000,
  maxConnections = 20
)

val db = Database.forConfig("mydb", executor = executor)

// HikariCP configuration in application.conf
/*
mydb {
  url = "jdbc:postgresql://localhost/test"
  driver = "org.postgresql.Driver"
  user = "postgres" 
  password = "password"
  connectionPool = HikariCP
  
  hikaricp {
    maxConnections = 20
    minConnections = 5
    connectionTimeout = 30000
    idleTimeout = 600000
    maxLifetime = 1800000
    leakDetectionThreshold = 60000
  }
  
  numThreads = 10
  maxConnections = 20
  queueSize = 5000
}
*/

Database Configuration

Configure database connections using Typesafe Config.

/**
 * Database configuration management
 */
case class DatabaseConfig[P <: BasicProfile](profile: P, db: P#Backend#Database, config: Config) {
  /** Get the profile API */
  def api = profile.api
}

object DatabaseConfig {
  /**
   * Load database configuration for a specific profile
   */
  def forConfig[P <: BasicProfile](path: String, config: Config = ConfigFactory.load())(implicit profileTag: ClassTag[P]): DatabaseConfig[P]
}

Usage Examples:

// Type-safe configuration
val dbConfig = DatabaseConfig.forConfig[JdbcProfile]("mydb")
import dbConfig.profile.api._
val db = dbConfig.db

// Use with dependency injection
class UserService(dbConfig: DatabaseConfig[JdbcProfile]) {
  import dbConfig.profile.api._
  private val db = dbConfig.db
  
  def findUser(id: Int): Future[Option[User]] = {
    db.run(users.filter(_.id === id).result.headOption)
  }
}

// Configuration file structure:
/*
mydb {
  profile = "slick.jdbc.PostgresProfile$"
  db {
    url = "jdbc:postgresql://localhost/test"
    driver = "org.postgresql.Driver"
    user = "postgres"
    password = "password"
    connectionPool = HikariCP
    maxConnections = 20
    minConnections = 5
  }
}
*/

Capabilities System

Database capabilities define what features are supported by each database.

/**
 * Base trait for database capabilities
 */
trait Capability

/**
 * Standard relational capabilities
 */
object RelationalCapabilities {
  /** Join operations support */
  case object join extends Capability
  
  /** Foreign key constraints */
  case object foreignKeyConstraints extends Capability
  
  /** Full outer join support */
  case object fullOuterJoin extends Capability
  
  /** Union operations */
  case object union extends Capability
  
  /** Sequences/auto-increment */
  case object sequence extends Capability
}

/**
 * JDBC-specific capabilities
 */
object JdbcCapabilities {
  /** Prepared statement support */
  case object preparedStatement extends Capability
  
  /** Batch operations */
  case object batchUpdates extends Capability
  
  /** Transaction isolation levels */
  case object transactionIsolation extends Capability
  
  /** Return generated keys */
  case object returnGeneratedKeys extends Capability
}

/**
 * Profile capability checking
 */
trait ProfileCapabilities {
  /** Check if a capability is supported */
  def supports(capability: Capability): Boolean
  
  /** Get all supported capabilities */
  def capabilities: Set[Capability]
}

Usage Examples:

// Check profile capabilities
val profile = PostgresProfile
val supportsFullOuterJoin = profile.capabilities.supports(RelationalCapabilities.fullOuterJoin)

// Conditional query based on capabilities  
val query = if (profile.capabilities.supports(RelationalCapabilities.fullOuterJoin)) {
  users.joinFull(orders).on(_.id === _.userId)
} else {
  users.joinLeft(orders).on(_.id === _.userId)
}

// Feature detection
def buildOptimalQuery[P <: JdbcProfile](profile: P) = {
  import profile.api._
  
  if (profile.capabilities.supports(JdbcCapabilities.batchUpdates)) {
    // Use batch operations
    users ++= userList
  } else {
    // Fall back to individual inserts
    DBIO.sequence(userList.map(user => users += user))
  }
}

Memory Profile

In-memory database profile for testing and development.

/**
 * Memory database profile for testing
 */
object MemoryProfile extends BasicProfile {
  override val api = new API {}
  
  /** Memory-specific query execution */
  type QueryExecutor = MemoryQueryExecutor
  
  /** Memory backend */
  type Backend = MemoryBackend
}

/**
 * Memory backend for in-memory operations
 */
trait MemoryBackend extends BasicBackend {
  type Database = MemoryDatabase
  type Session = MemorySession
}

Usage Examples:

// Use memory profile for testing
import slick.memory.MemoryProfile.api._

class UserServiceTest extends TestSuite {
  val db = Database.forConfig("memory-test")
  
  // Configuration for memory database:
  /*
  memory-test {
    profile = "slick.memory.MemoryProfile$"
    db {
      connectionPool = disabled
    }
  }
  */
  
  test("user operations") {
    val users = TableQuery[Users]
    
    val testData = for {
      _ <- users.schema.create
      _ <- users += User("Alice")
      user <- users.filter(_.name === "Alice").result.head
    } yield user
    
    val result = db.run(testData)
    // ... assertions
  }
}

Session Management

Low-level session management for advanced use cases.

/**
 * Database session for low-level operations
 */
trait Session {
  /** Execute a statement and return results */
  def withStatement[T](sql: String)(f: Statement => T): T
  
  /** Execute a prepared statement */
  def withPreparedStatement[T](sql: String)(f: PreparedStatement => T): T
  
  /** Get the underlying JDBC connection */
  def conn: Connection
  
  /** Check if session is in a transaction */
  def inTransaction: Boolean
}

/**
 * Session-based database operations
 */
trait SessionDatabase {
  /** Run code with a session */
  def withSession[T](f: Session => T): T
  
  /** Run code in a transaction */
  def withTransaction[T](f: Session => T): T
}

Usage Examples:

// Low-level session operations (rarely needed)
db.withSession { implicit session =>
  // Direct JDBC operations
  session.withStatement("ANALYZE TABLE users") { stmt =>
    stmt.execute()
  }
  
  // Prepared statement
  session.withPreparedStatement("SELECT * FROM users WHERE id = ?") { stmt =>
    stmt.setInt(1, 123)
    val rs = stmt.executeQuery()
    // Process ResultSet...
  }
}

// Transaction with session
db.withTransaction { implicit session =>
  // Multiple operations in transaction
  val insertAction = users += User("Bob")
  val updateAction = users.filter(_.id === 1).map(_.name).update("Updated")
  
  // Execute with session
  insertAction.run
  updateAction.run
}

Types

trait BasicProfile {
  type Backend <: BasicBackend
  type Database = Backend#Database
  val api: API
}

trait RelationalProfile extends BasicProfile {
  type ColumnType[T] <: TypedType[T]
  type TableQuery[E <: AbstractTable[_]] <: Query[E, E#TableElementType, Seq]
}

trait JdbcProfile extends RelationalProfile {
  type Backend = JdbcBackend
  type ProfileAction[+R, +S <: NoStream, -E <: Effect] = DBIOAction[R, S, E] 
  type SimpleDBIO[+R] = ProfileAction[R, NoStream, Effect.All]
}

case class DatabaseConfig[P <: BasicProfile](profile: P, db: P#Backend#Database, config: Config)
case class AsyncExecutor(name: String, minThreads: Int, maxThreads: Int, queueSize: Int, maxConnections: Int)

trait Database {
  def run[R](a: DBIOAction[R, NoStream, Nothing]): Future[R]
  def stream[T](a: StreamingDBIO[_, T]): DatabasePublisher[T]
  def close(): Unit
}

trait Capability
trait ProfileCapabilities {
  def supports(capability: Capability): Boolean
  def capabilities: Set[Capability]
}