Database-specific profiles providing connection management, SQL generation, and database-specific features in Slick.
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]
}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]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)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
}
*/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
}
}
*/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))
}
}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
}
}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
}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]
}