├── .github ├── mergify.yml ├── scala-steward.conf ├── workflows │ ├── publish.yml │ ├── release-drafter.yml │ ├── dependency-graph.yml │ └── build-test.yml └── dependabot.yml ├── project ├── build.properties ├── Common.scala ├── plugins.sbt ├── Playdoc.scala ├── Dependencies.scala └── Omnidoc.scala ├── src ├── evolutions │ └── src │ │ ├── main │ │ ├── resources │ │ │ └── reference.conf │ │ └── scala │ │ │ └── play │ │ │ └── api │ │ │ └── db │ │ │ └── slick │ │ │ └── evolutions │ │ │ ├── SlickDBApi.scala │ │ │ ├── EvolutionsModule.scala │ │ │ └── internal │ │ │ └── DBApiAdapter.scala │ │ └── test │ │ ├── resources │ │ └── logback-test.xml │ │ └── scala │ │ └── play │ │ └── api │ │ └── db │ │ └── slick │ │ └── evolutions │ │ ├── DBApiAdapterSpec.scala │ │ └── EvolutionsModuleSpec.scala └── core │ └── src │ ├── main │ ├── scala │ │ └── play │ │ │ └── api │ │ │ └── db │ │ │ └── slick │ │ │ ├── package.scala │ │ │ ├── DbName.scala │ │ │ ├── SlickModule.scala │ │ │ ├── SlickApi.scala │ │ │ └── DatabaseConfigProvider.scala │ └── resources │ │ └── reference.conf │ └── test │ ├── scala │ └── play │ │ └── api │ │ └── db │ │ └── slick │ │ ├── util │ │ └── WithReferenceConfig.scala │ │ ├── TestData.scala │ │ ├── DatabaseConfigProviderSpec.scala │ │ ├── SlickApiSpec.scala │ │ ├── DefaultSlickApiSpec.scala │ │ └── SlickModuleSpec.scala │ └── resources │ └── logback-test.xml ├── .git-blame-ignore-revs ├── docs ├── manual │ └── working │ │ └── scalaGuide │ │ └── main │ │ └── sql │ │ └── slick │ │ ├── images │ │ └── database-config-error.png │ │ ├── code │ │ ├── scalaguide │ │ │ └── slick │ │ │ │ └── views │ │ │ │ └── index.scala.html │ │ ├── slick.sbt │ │ ├── UsersSchema.scala │ │ ├── Example.scala │ │ └── DI.scala │ │ ├── index.toc │ │ ├── PlaySlickAdvancedTopics.md │ │ ├── PlaySlickFAQ.md │ │ ├── PlaySlick.md │ │ └── PlaySlickMigrationGuide.md └── build.sbt ├── .gitignore ├── .scalafmt.conf ├── README.md └── LICENSE /.github/mergify.yml: -------------------------------------------------------------------------------- 1 | extends: .github 2 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.7 2 | -------------------------------------------------------------------------------- /src/evolutions/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | play { 2 | modules { 3 | enabled += "play.api.db.slick.evolutions.EvolutionsModule" 4 | } 5 | } -------------------------------------------------------------------------------- /src/core/src/main/scala/play/api/db/slick/package.scala: -------------------------------------------------------------------------------- 1 | package play.api.db 2 | 3 | package object slick { 4 | private[slick] val IssueTracker = "https://github.com/playframework/play-slick/issues" 5 | } 6 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Scala Steward: Reformat with scalafmt 3.8.3 2 | 28e54446e0830b27d6f861bcdf10131fadce91d9 3 | 4 | # Scala Steward: Reformat with scalafmt 3.9.8 5 | a0423c4d4be798c5f3be09e4a94570a557348f31 6 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/images/database-config-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/playframework/play-slick/main/docs/manual/working/scalaGuide/main/sql/slick/images/database-config-error.png -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/code/scalaguide/slick/views/index.scala.html: -------------------------------------------------------------------------------- 1 | @import scalaguide.slick.UsersSchema.User 2 | 3 | @(users: Seq[User]) 4 | 5 | @for(user <- users) { 6 | @user.name @user.surname 7 | } 8 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/index.toc: -------------------------------------------------------------------------------- 1 | PlaySlick:Using Play Slick 2 | PlaySlickMigrationGuide:Play Slick migration guide 3 | PlaySlickAdvancedTopics: Play Slick advanced topics 4 | PlaySlickFAQ: Play Slick FAQ 5 | -------------------------------------------------------------------------------- /.github/scala-steward.conf: -------------------------------------------------------------------------------- 1 | commits.message = "${artifactName} ${nextVersion} (was ${currentVersion})" 2 | 3 | pullRequests.grouping = [ 4 | { name = "patches", "title" = "Patch updates", "filter" = [{"version" = "patch"}] } 5 | ] 6 | -------------------------------------------------------------------------------- /src/core/src/main/scala/play/api/db/slick/DbName.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | /** The name used in the application's config file to reference a slick database configuration. */ 4 | case class DbName(val value: String) extends AnyVal 5 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | push: 5 | branches: # Snapshots 6 | - main 7 | tags: ["**"] # Releases 8 | 9 | jobs: 10 | publish-artifacts: 11 | name: Publish / Artifacts 12 | uses: playframework/.github/.github/workflows/publish.yml@v4 13 | secrets: inherit 14 | -------------------------------------------------------------------------------- /docs/build.sbt: -------------------------------------------------------------------------------- 1 | name := "play-slick-docs" 2 | 3 | libraryDependencies += component("play-specs2") % "test" 4 | 5 | PlayDocsKeys.javaManualSourceDirectories := (baseDirectory.value / "manual" / "working" / "javaGuide" ** "code").get 6 | PlayDocsKeys.scalaManualSourceDirectories := (baseDirectory.value / "manual" / "working" / "scalaGuide" ** "code").get 7 | -------------------------------------------------------------------------------- /src/evolutions/src/main/scala/play/api/db/slick/evolutions/SlickDBApi.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick.evolutions 2 | 3 | import play.api.db.slick.SlickApi 4 | import play.api.db.DBApi 5 | import play.api.db.slick.evolutions.internal.DBApiAdapter 6 | 7 | object SlickDBApi { 8 | def apply(slickApi: SlickApi): DBApi = new DBApiAdapter(slickApi) 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache 6 | .classpath 7 | .target 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # Scala-IDE specific 16 | .scala_dependencies 17 | .project 18 | .settings 19 | .cache-main 20 | .cache-tests 21 | bin 22 | 23 | # IntelliJ IDEA specific 24 | .idea 25 | 26 | .bsp/ 27 | -------------------------------------------------------------------------------- /project/Common.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | import sbt.Keys._ 4 | 5 | object Common extends AutoPlugin { 6 | 7 | val repoName = "play-slick" 8 | 9 | override def requires = plugins.JvmPlugin 10 | override def trigger = allRequirements 11 | 12 | override def projectSettings = Seq( 13 | Test / parallelExecution := false, 14 | Test / fork := true 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/code/slick.sbt: -------------------------------------------------------------------------------- 1 | // #add-library-dependencies 2 | libraryDependencies += "org.playframework" %% "play-slick" % "6.2.0" 3 | // #add-library-dependencies 4 | 5 | // #add-dependency-with-evolutions 6 | libraryDependencies ++= Seq( 7 | "org.playframework" %% "play-slick" % "6.2.0", 8 | "org.playframework" %% "play-slick-evolutions" % "6.2.0" 9 | ) 10 | // #add-dependency-with-evolutions 11 | -------------------------------------------------------------------------------- /src/core/src/test/scala/play/api/db/slick/util/WithReferenceConfig.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick.util 2 | 3 | import org.specs2.specification.Scope 4 | 5 | import play.api.Configuration 6 | 7 | trait WithReferenceConfig extends Scope { 8 | val ref = Configuration.reference 9 | def enabledModules(c: Configuration): List[String] = { 10 | ref.get[Seq[String]]("play.modules.enabled").toList 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | target-branch: "6.2.x" 12 | commit-message: 13 | prefix: "[6.2.x] " 14 | - package-ecosystem: "github-actions" 15 | directory: "/" 16 | schedule: 17 | interval: "weekly" 18 | target-branch: "5.4.x" 19 | commit-message: 20 | prefix: "[5.4.x] " 21 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | update_release_draft: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: release-drafter/release-drafter@v6 13 | with: 14 | name: "Play Slick $RESOLVED_VERSION" 15 | config-name: release-drafts/increasing-major-version.yml # located in .github/ in the default branch within this or the .github repo 16 | commitish: ${{ github.ref_name }} 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /src/core/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | play { 2 | modules { 3 | enabled += "play.api.db.slick.SlickModule" 4 | } 5 | 6 | # play-slick config 7 | slick { 8 | db { 9 | # The name of the configuration item from which to read databases config. 10 | # So, if set to slick.dbs, means that slick.dbs.default is where the 11 | # configuration for the database named default is found. 12 | config = "slick.dbs" 13 | 14 | # The name of the default database, used when no database name is explicitly 15 | # specified. 16 | default = "default" 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers ++= DefaultOptions.resolvers(snapshot = true) 2 | resolvers ++= Seq( 3 | Resolver.sonatypeCentralSnapshots, // used by deploy nightlies, which publish here & use -Dplay.version 4 | ) 5 | 6 | addSbtPlugin("org.playframework" % "sbt-plugin" % sys.props.getOrElse("play.version", "3.1.0-M4")) 7 | addSbtPlugin("org.playframework" % "play-docs-sbt-plugin" % sys.props.getOrElse("play.version", "3.1.0-M4")) 8 | 9 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.6") 10 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4") 11 | 12 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.2") 13 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/code/UsersSchema.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 3 | */ 4 | package scalaguide.slick 5 | 6 | import slick.jdbc.H2Profile.api._ 7 | 8 | object UsersSchema { 9 | 10 | case class User(name: String, surname: String) 11 | 12 | class UsersTable(tag: Tag) extends Table[User](tag, "USER") { 13 | def name = column[String]("name", O.PrimaryKey) 14 | def surname = column[String]("surname") 15 | def * = (name, surname) <> (User.tupled, User.unapply) 16 | } 17 | 18 | val Users = TableQuery[UsersTable] 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/dependency-graph.yml: -------------------------------------------------------------------------------- 1 | name: Dependency Graph 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | concurrency: 8 | # Only run once for latest commit per ref and cancel other (previous) runs. 9 | group: dependency-graph-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | permissions: 13 | contents: write # this permission is needed to submit the dependency graph 14 | 15 | jobs: 16 | dependency-graph: 17 | name: Submit dependencies to GitHub 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v6 21 | with: 22 | fetch-depth: 0 23 | ref: ${{ inputs.ref }} 24 | - uses: sbt/setup-sbt@v1 25 | - uses: scalacenter/sbt-dependency-submission@v3 26 | -------------------------------------------------------------------------------- /src/evolutions/src/main/scala/play/api/db/slick/evolutions/EvolutionsModule.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick.evolutions 2 | 3 | import jakarta.inject.Singleton 4 | import play.api.Configuration 5 | import play.api.Environment 6 | import play.api.db.DBApi 7 | import play.api.db.slick.SlickApi 8 | import play.api.db.slick.evolutions.internal.DBApiAdapter 9 | import play.api.inject.Binding 10 | import play.api.inject.Module 11 | 12 | @Singleton 13 | class EvolutionsModule extends Module { 14 | def bindings(environment: Environment, configuration: Configuration): Seq[Binding[?]] = { 15 | Seq(bind[DBApi].to[DBApiAdapter].in[Singleton]) 16 | } 17 | } 18 | 19 | /** 20 | * Helper to provide Slick implementation of DBApi. 21 | */ 22 | trait SlickEvolutionsComponents { 23 | def slickApi: SlickApi 24 | 25 | lazy val dbApi: DBApi = SlickDBApi(slickApi) 26 | } 27 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | # This file was originally copied from https://github.com/playframework/playframework/blob/main/.scalafmt.conf 2 | version = 3.10.2 3 | runner.dialect = scala213 4 | align.preset = more 5 | assumeStandardLibraryStripMargin = true 6 | danglingParentheses.preset = true 7 | docstrings.style = Asterisk 8 | maxColumn = 120 9 | project.git = true 10 | rewrite.rules = [ AvoidInfix, ExpandImportSelectors, RedundantParens, SortModifiers, PreferCurlyFors ] 11 | rewrite.sortModifiers.order = [ "private", "protected", "final", "sealed", "abstract", "implicit", "override", "lazy" ] 12 | spaces.inImportCurlyBraces = true # more idiomatic to include whitepsace in import x.{ yyy } 13 | trailingCommas = preserve 14 | rewrite.scala3.convertToNewSyntax = true 15 | rewrite.scala3.newSyntax.control = false 16 | runner.dialectOverride { 17 | allowSignificantIndentation = false 18 | allowAsForImportRename = false 19 | allowStarWildcardImport = false 20 | } 21 | -------------------------------------------------------------------------------- /project/Playdoc.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | import sbt.io.IO 4 | 5 | object Playdoc extends AutoPlugin { 6 | 7 | object autoImport { 8 | final val Docs = config("docs") 9 | val playdocDirectory = settingKey[File]("Base directory of play documentation") 10 | val playdocPackage = taskKey[File]("Package play documentation") 11 | } 12 | 13 | import autoImport.* 14 | 15 | override def requires = sbt.plugins.JvmPlugin 16 | 17 | override def trigger = noTrigger 18 | 19 | override def projectSettings = 20 | Defaults.packageTaskSettings(playdocPackage, playdocPackage / mappings) ++ 21 | Seq( 22 | playdocDirectory := (ThisBuild / baseDirectory).value / "docs" / "manual", 23 | playdocPackage / mappings := { 24 | val base: File = playdocDirectory.value 25 | base.allPaths.pair(IO.relativize(base.getParentFile(), _)) 26 | }, 27 | playdocPackage / artifactClassifier := Some("playdoc"), 28 | playdocPackage / artifact ~= { _.withConfigurations(Vector(Docs)) } 29 | ) ++ 30 | addArtifact(playdocPackage / artifact, playdocPackage) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/code/Example.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 3 | */ 4 | package scalaguide.slick 5 | package global 6 | 7 | import jakarta.inject.Inject 8 | 9 | import play.api.db.slick.DatabaseConfigProvider 10 | import play.api.db.slick.HasDatabaseConfigProvider 11 | import play.api.mvc._ 12 | import slick.jdbc.JdbcProfile 13 | 14 | import scala.concurrent.ExecutionContext 15 | import scala.concurrent.Future 16 | import scalaguide.slick.UsersSchema._ 17 | 18 | class Application1 @Inject() (protected val dbConfigProvider: DatabaseConfigProvider, cc: ControllerComponents)(implicit 19 | ec: ExecutionContext 20 | ) extends AbstractController(cc) 21 | with HasDatabaseConfigProvider[JdbcProfile] { 22 | 23 | // #driver-import 24 | import dbConfig.profile.api._ 25 | // #driver-import 26 | 27 | // #action-with-db 28 | def index(name: String) = Action.async { implicit request => 29 | val resultingUsers: Future[Seq[User]] = db.run(Users.filter(_.name === name).result) 30 | resultingUsers.map(users => Ok(views.html.index(users))) 31 | } 32 | // #action-with-db 33 | } 34 | -------------------------------------------------------------------------------- /src/core/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | %highlight(%-5level) %logger{15} - %message%n%xException{10} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/evolutions/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | %highlight(%-5level) %logger{15} - %message%n%xException{10} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /.github/workflows/build-test.yml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | pull_request: 5 | 6 | push: 7 | branches: 8 | - main # Check branch after merge 9 | 10 | concurrency: 11 | # Only run once for latest commit per ref and cancel other (previous) runs. 12 | group: ci-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | check-code-style: 17 | name: Code Style 18 | uses: playframework/.github/.github/workflows/cmd.yml@v4 19 | with: 20 | cmd: sbt scalafmtCheckAll scalafmtSbtCheck 21 | 22 | check-binary-compatibility: 23 | name: Binary Compatibility 24 | uses: playframework/.github/.github/workflows/binary-check.yml@v4 25 | 26 | check-docs: 27 | name: Docs 28 | uses: playframework/.github/.github/workflows/cmd.yml@v4 29 | with: 30 | cmd: sbt docs/scalafmtCheckAll docs/scalafmtSbtCheck docs/test docs/validateDocs 31 | 32 | tests: 33 | name: Tests 34 | needs: 35 | - "check-code-style" 36 | - "check-binary-compatibility" 37 | - "check-docs" 38 | uses: playframework/.github/.github/workflows/cmd.yml@v4 39 | with: 40 | java: 21, 17 41 | scala: 2.13.x, 3.x 42 | cmd: sbt ++$MATRIX_SCALA test 43 | 44 | finish: 45 | name: Finish 46 | if: github.event_name == 'pull_request' 47 | needs: # Should be last 48 | - "tests" 49 | uses: playframework/.github/.github/workflows/rtm.yml@v4 50 | -------------------------------------------------------------------------------- /project/Dependencies.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | object Dependencies { 4 | val core = Seq( 5 | Library.slick, 6 | Library.slickHikariCP, 7 | Library.playCore, 8 | Library.playJdbcApi, 9 | Library.playLogback % "test", 10 | Library.playSpecs2 % "test", 11 | Library.h2 % "test" 12 | ) 13 | 14 | val evolutions = Seq( 15 | Library.playJdbcEvolutions, 16 | Library.h2 % "test", // DBApiAdapterSpec requires a database to be available, so that a connection can be made 17 | Library.playSpecs2 % "test" 18 | ) 19 | } 20 | 21 | object Version { 22 | val play = _root_.play.core.PlayVersion.current 23 | 24 | val slick = "3.6.1" 25 | val h2 = "2.4.240" 26 | } 27 | 28 | object Library { 29 | val playLogback = "org.playframework" %% "play-logback" % Version.play 30 | val playCore = "org.playframework" %% "play" % Version.play 31 | val playJdbcApi = "org.playframework" %% "play-jdbc-api" % Version.play 32 | val playJdbcEvolutions = "org.playframework" %% "play-jdbc-evolutions" % Version.play 33 | val playSpecs2 = "org.playframework" %% "play-specs2" % Version.play 34 | val slick = "com.typesafe.slick" %% "slick" % Version.slick 35 | val slickHikariCP = "com.typesafe.slick" %% "slick-hikaricp" % Version.slick 36 | val h2 = "com.h2database" % "h2" % Version.h2 37 | } 38 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/code/DI.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) from 2022 The Play Framework Contributors , 2011-2021 Lightbend Inc. 3 | */ 4 | package scalaguide.slick 5 | package di 6 | 7 | import jakarta.inject.Inject 8 | 9 | import scala.concurrent.ExecutionContext 10 | import scala.concurrent.Future 11 | import play.api.mvc._ 12 | import play.api.db.slick.DatabaseConfigProvider 13 | import play.api.db.slick.HasDatabaseConfigProvider 14 | 15 | import slick.jdbc.JdbcProfile 16 | import UsersSchema._ 17 | 18 | //#di-database-config 19 | class Application @Inject() (protected val dbConfigProvider: DatabaseConfigProvider, cc: ControllerComponents)(implicit 20 | ec: ExecutionContext 21 | ) extends AbstractController(cc) 22 | with HasDatabaseConfigProvider[JdbcProfile] { 23 | // #di-database-config 24 | 25 | import profile.api._ 26 | 27 | def index(name: String) = Action.async { implicit request => 28 | val resultingUsers: Future[Seq[User]] = db.run(Users.filter(_.name === name).result) 29 | resultingUsers.map(users => Ok(views.html.index(users))) 30 | } 31 | } 32 | 33 | import play.db.NamedDatabase 34 | //#named-di-database-config 35 | class Application2 @Inject() ( 36 | @NamedDatabase("") protected val dbConfigProvider: DatabaseConfigProvider, 37 | cc: ControllerComponents 38 | )(implicit ec: ExecutionContext) 39 | extends AbstractController(cc) 40 | with HasDatabaseConfigProvider[JdbcProfile] { 41 | // #named-di-database-config 42 | } 43 | -------------------------------------------------------------------------------- /src/core/src/test/scala/play/api/db/slick/TestData.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import play.api.Configuration 4 | 5 | object TestData { 6 | private lazy val h2DatasourceConfig: Configuration = Configuration.from( 7 | Map( 8 | "slick.dbs.h2datasource.profile" -> "slick.jdbc.H2Profile$", 9 | "slick.dbs.h2datasource.db.dataSourceClass" -> "slick.jdbc.DatabaseUrlDataSource", 10 | "slick.dbs.h2datasource.db.properties.driver" -> "org.h2.Driver", 11 | "slick.dbs.h2datasource.db.properties.url" -> "jdbc:h2:mem:" 12 | ) 13 | ) 14 | 15 | val configuration: Configuration = h2DatasourceConfig.withFallback( 16 | Configuration.from( 17 | Map( 18 | "slick.dbs.somedb.profile" -> "slick.jdbc.H2Profile$", 19 | "slick.dbs.somedb.db.driver" -> "org.h2.Driver", 20 | "slick.dbs.somedb.db.url" -> "jdbc:h2:mem:", 21 | "slick.dbs.default.profile" -> "slick.jdbc.MySQLProfile$", 22 | "slick.dbs.default.db.driver" -> "com.mysql.jdbc.Driver", 23 | "slick.dbs.jdbc-driver-not-recognized.profile" -> "slick.jdbc.MySQLProfile$", 24 | "slick.dbs.jdbc-driver-not-recognized.db.driver" -> "play.api.db.slick.SomeDummyDriver" 25 | ) 26 | ) 27 | ) 28 | 29 | val badConfiguration: Configuration = Configuration 30 | .from(Map("slick.dbs.missing-slick-profile.db.driver" -> "play.api.db.slick.SomeDummyDriver")) 31 | .withFallback(configuration) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/core/src/test/scala/play/api/db/slick/DatabaseConfigProviderSpec.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import org.specs2.mutable.Specification 4 | 5 | import play.api.Application 6 | import play.api.Mode 7 | import play.api.inject.guice.GuiceApplicationBuilder 8 | import slick.basic.BasicProfile 9 | 10 | class DatabaseConfigProviderSpec extends Specification { 11 | 12 | def withApp[T](block: Application => T): T = { 13 | val app = new GuiceApplicationBuilder() 14 | .configure(TestData.configuration) 15 | .in(Mode.Test) 16 | .build() 17 | 18 | try { 19 | block(app) 20 | } finally { 21 | app.stop() 22 | } 23 | } 24 | "DatabaseConfigProvider" should { 25 | "return the configured slick profile for the given database" in withApp { implicit app => 26 | val config = DatabaseConfigProvider.get[BasicProfile]("somedb") 27 | val profile = config.profile 28 | profile must equalTo(_root_.slick.jdbc.H2Profile) 29 | } 30 | 31 | "return the configured profile for the default database when db name is not specified" in withApp { implicit app => 32 | val config = DatabaseConfigProvider.get[BasicProfile] 33 | val profile = config.profile 34 | profile must equalTo(_root_.slick.jdbc.MySQLProfile) 35 | } 36 | 37 | "throw when accessing the db if an invalid jdbc driver is configured" in withApp { implicit app => 38 | val config = DatabaseConfigProvider.get[BasicProfile]("jdbc-driver-not-recognized") 39 | config.db must throwA[Throwable]("""Failed to load driver class play.api.db.slick.SomeDummyDriver""") 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/evolutions/src/test/scala/play/api/db/slick/evolutions/DBApiAdapterSpec.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick.evolutions 2 | 3 | import org.specs2.mutable.Specification 4 | import play.api.db.DBApi 5 | import play.api.db.slick.TestData 6 | import play.api.inject.guice.GuiceApplicationBuilder 7 | 8 | class DBApiAdapterSpec extends Specification { 9 | 10 | "DBApiAdapter" >> { 11 | val appBuilder = GuiceApplicationBuilder(configuration = TestData.configuration) 12 | val injector = appBuilder.injector() 13 | 14 | val api = injector.instanceOf[DBApi] 15 | val dbName = "somedb" 16 | val db = api.database(dbName) 17 | 18 | "getConnection" should { 19 | "respect autocommit parameter" in { 20 | db.getConnection(autocommit = false).getAutoCommit must_== false 21 | db.getConnection(autocommit = true).getAutoCommit must_== true 22 | } 23 | 24 | "default autocommit to true" in { 25 | db.getConnection().getAutoCommit must_== true 26 | } 27 | } 28 | 29 | "withConnection" should { 30 | "respect autocommit parameter" in { 31 | var called = false 32 | db.withConnection(autocommit = false) { conn => 33 | conn.getAutoCommit must_== false 34 | called = true 35 | } 36 | called must_== true 37 | 38 | called = false 39 | db.withConnection(autocommit = true) { conn => 40 | conn.getAutoCommit must_== true 41 | called = true 42 | } 43 | called must_== true 44 | } 45 | 46 | "default autocommit to true" in { 47 | var called = false 48 | db.withConnection { conn => 49 | conn.getAutoCommit must_== true 50 | called = true 51 | } 52 | called must_== true 53 | } 54 | } 55 | 56 | "url" should { 57 | "return the value set in the config for the jdbc url" in { 58 | db.url must_== TestData.configuration.get[String](s"slick.dbs.$dbName.db.url") 59 | } 60 | "return the value set in the config for the datasource url" in { 61 | val h2DatasourceDb = api.database("h2datasource") 62 | h2DatasourceDb.url must_== TestData.configuration.get[String](s"slick.dbs.h2datasource.db.properties.url") 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/evolutions/src/test/scala/play/api/db/slick/evolutions/EvolutionsModuleSpec.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick.evolutions 2 | 3 | import org.specs2.mutable.Specification 4 | import play.api.Configuration 5 | import play.api.Environment 6 | import play.api.db.DBApi 7 | import play.api.db.slick.SlickComponents 8 | import play.api.db.slick.TestData 9 | import play.api.db.slick.evolutions.internal.DBApiAdapter 10 | import play.api.inject.ApplicationLifecycle 11 | import play.api.inject.DefaultApplicationLifecycle 12 | import play.api.inject.guice.GuiceApplicationBuilder 13 | import play.api.db.slick.util.WithReferenceConfig 14 | 15 | import scala.concurrent.ExecutionContext 16 | 17 | class EvolutionsModuleSpec extends Specification { 18 | 19 | "reference.conf" should { 20 | "evolutions module is enabled" in new WithReferenceConfig { 21 | enabledModules(ref) must contain(classOf[EvolutionsModule].getName) 22 | } 23 | } 24 | 25 | "EvolutionsModule" should { 26 | val appBuilder = GuiceApplicationBuilder(configuration = TestData.configuration) 27 | val injector = appBuilder.injector() 28 | 29 | "bind DBApi to DBApiAdapter" in { 30 | val api = injector.instanceOf[DBApi] 31 | api must beAnInstanceOf[DBApiAdapter] 32 | } 33 | "bind DBApi as a singleton" in { 34 | val api1 = injector.instanceOf[DBApi] 35 | val api2 = injector.instanceOf[DBApi] 36 | api1 mustEqual api2 37 | } 38 | } 39 | 40 | "SlickEvolutionsComponents" should { 41 | object TestComponents extends SlickComponents with SlickEvolutionsComponents { 42 | override def environment: Environment = Environment.simple() 43 | 44 | override def applicationLifecycle: ApplicationLifecycle = new DefaultApplicationLifecycle 45 | 46 | override def configuration: Configuration = TestData.configuration 47 | 48 | override def executionContext: ExecutionContext = 49 | ExecutionContext.Implicits.global // using the global EC since this is test code 50 | } 51 | 52 | "bind DBApi to DBApiAdapter" in { 53 | val api = TestComponents.dbApi 54 | api must beAnInstanceOf[DBApiAdapter] 55 | } 56 | "bind DBApi as a singleton" in { 57 | val api1 = TestComponents.dbApi 58 | val api2 = TestComponents.dbApi 59 | api1 mustEqual api2 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /project/Omnidoc.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import sbt.Keys._ 3 | import sbt.Package.ManifestAttributes 4 | 5 | /** 6 | * This AutoPlugin adds the `Omnidoc-Source-URL` key on the MANIFEST.MF of artifact-sources.jar so later Omnidoc can use 7 | * that value to link scaladocs to GitHub sources. 8 | */ 9 | object Omnidoc extends AutoPlugin { 10 | 11 | object autoImport { 12 | lazy val omnidocSnapshotBranch = settingKey[String]("Git branch for development versions") 13 | lazy val omnidocPathPrefix = settingKey[String]("Prefix before source directory paths") 14 | lazy val omnidocSourceUrl = settingKey[Option[String]]("Source URL for scaladoc linking") 15 | } 16 | 17 | val omnidocGithubRepo: Option[String] = Some(s"playframework/${Common.repoName}") 18 | 19 | val omnidocTagPrefix: Option[String] = Some("") 20 | 21 | val SourceUrlKey = "Omnidoc-Source-URL" 22 | 23 | override def requires = sbt.plugins.JvmPlugin 24 | 25 | override def trigger = noTrigger 26 | 27 | import autoImport.* 28 | 29 | override def projectSettings = Seq( 30 | omnidocSourceUrl := omnidocGithubRepo.map { repo => 31 | val development: String = (omnidocSnapshotBranch ?? "main").value 32 | val tagged: String = omnidocTagPrefix.getOrElse("v") + version.value 33 | val tree: String = if (isSnapshot.value) development else tagged 34 | val prefix: String = "/" + (omnidocPathPrefix ?? "").value 35 | val path: String = { 36 | val buildDir: File = (ThisBuild / baseDirectory).value 37 | val projDir: File = baseDirectory.value 38 | val rel: Option[String] = IO.relativize(buildDir, projDir) 39 | rel match { 40 | case None if buildDir == projDir => "" // Same dir (sbt 0.13) 41 | case Some("") => "" // Same dir (sbt 1.0) 42 | case Some(childDir) => prefix + childDir // Child dir 43 | case None => "" // Disjoint dirs (Rich: I'm not sure if this can happen) 44 | } 45 | } 46 | s"https://github.com/${repo}/tree/${tree}${path}" 47 | }, 48 | Compile / packageSrc / packageOptions ++= omnidocSourceUrl.value.toSeq.map { url => 49 | ManifestAttributes(SourceUrlKey -> url) 50 | } 51 | ) 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/core/src/test/scala/play/api/db/slick/SlickApiSpec.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import org.specs2.mutable.Specification 4 | 5 | import play.api.Configuration 6 | import play.api.PlayException 7 | import play.api.inject.guice.GuiceApplicationBuilder 8 | import slick.basic.BasicProfile 9 | 10 | class SlickApiSpec extends Specification { 11 | trait SUT { 12 | def config: Configuration 13 | val appBuilder = new GuiceApplicationBuilder(configuration = config) 14 | val injector = appBuilder.injector() 15 | val api = injector.instanceOf[SlickApi] 16 | } 17 | 18 | object SUTWithGoodConfig extends SUT { 19 | def config: Configuration = TestData.configuration 20 | } 21 | 22 | object SUTWithBadConfig extends SUT { 23 | def config: Configuration = TestData.badConfiguration 24 | } 25 | 26 | "SlickApi.dbConfig" should { 27 | "return a DatabaseConfig instance for a correctly configured database" in { 28 | import SUTWithGoodConfig._ 29 | val default = api.dbConfig[BasicProfile](DbName("default")) 30 | default must not(beNull) 31 | } 32 | "always return the same DatabaseConfig instance for a given database name" in { 33 | import SUTWithGoodConfig._ 34 | val default1 = api.dbConfig[BasicProfile](DbName("default")) 35 | val default2 = api.dbConfig[BasicProfile](DbName("default")) 36 | default1 mustEqual default2 37 | } 38 | "throw if a database name doesn't exist in the config" in { 39 | import SUTWithBadConfig._ 40 | api.dbConfig[BasicProfile](DbName("not-existing")) must throwA[PlayException] 41 | } 42 | "throw if a database config doesn't define a Slick profile" in { 43 | import SUTWithBadConfig._ 44 | api.dbConfig[BasicProfile](DbName("missing-slick-profile")) must throwA[PlayException] 45 | } 46 | } 47 | 48 | "SlickApi.dbConfigs" should { 49 | "return all DatabaseConfig instances for a valid configuration" in { 50 | import SUTWithGoodConfig._ 51 | api.dbConfigs[BasicProfile]() must have size 4 52 | } 53 | "throw if a database name doesn't exist in the config" in { 54 | import SUTWithBadConfig._ 55 | api.dbConfigs[BasicProfile]() must throwA[PlayException] 56 | } 57 | "throw if a database config doesn't define a Slick profile" in { 58 | import SUTWithBadConfig._ 59 | api.dbConfigs[BasicProfile]() must throwA[PlayException] 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/core/src/test/scala/play/api/db/slick/DefaultSlickApiSpec.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import java.util.concurrent.ConcurrentLinkedDeque 4 | 5 | import org.specs2.mutable.Specification 6 | import play.api.inject.Injector 7 | import play.api.inject.ApplicationLifecycle 8 | import play.api.inject.DefaultApplicationLifecycle 9 | import play.api.inject.guice.GuiceApplicationBuilder 10 | import slick.basic.BasicProfile 11 | 12 | class DefaultSlickApiSpec extends Specification { self => 13 | 14 | sequential 15 | 16 | // A new injector should be created to ensure each test is independent of each other 17 | def injector: Injector = GuiceApplicationBuilder(configuration = TestData.configuration).injector() 18 | 19 | def hooks(lifecycle: DefaultApplicationLifecycle): Seq[?] = { 20 | val hooksField = lifecycle.getClass.getDeclaredField("hooks") 21 | hooksField.setAccessible(true) 22 | hooksField.get(lifecycle).asInstanceOf[ConcurrentLinkedDeque[?]].toArray().toSeq 23 | } 24 | 25 | "DefaultSlickApi" should { 26 | 27 | "check the assumption that the ApplicationLifecycle isn't null" in { 28 | val lifecycle = injector.instanceOf[ApplicationLifecycle] 29 | lifecycle must not(beNull) 30 | } 31 | 32 | "check the assumption that the ApplicationLifecycle is a singleton" in { 33 | val injector = self.injector 34 | val lifecycle1 = injector.instanceOf[ApplicationLifecycle] 35 | val lifecycle2 = injector.instanceOf[ApplicationLifecycle] 36 | lifecycle1 mustEqual lifecycle2 37 | } 38 | 39 | "check the assumption that ApplicationLifecycle is binded to a DefaultApplicationLifecycle" in { 40 | val lifecycle = injector.instanceOf[ApplicationLifecycle] 41 | lifecycle must beAnInstanceOf[DefaultApplicationLifecycle] 42 | } 43 | 44 | "check that a stop hook is registered by SlickApi in the ApplicationLifecycle when a database is created" in { 45 | val injector = self.injector 46 | val lifecycle = injector.instanceOf[ApplicationLifecycle].asInstanceOf[DefaultApplicationLifecycle] 47 | 48 | // collect the num of existing hooks added by Play 49 | val originalNumOfHooks = hooks(lifecycle).length 50 | 51 | val api = injector.instanceOf[SlickApi] 52 | api.dbConfig[BasicProfile](DbName("default")) 53 | 54 | // confirm that SlickApi is adding its own hook 55 | hooks(lifecycle) must have size (originalNumOfHooks + 1) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/core/src/main/scala/play/api/db/slick/SlickModule.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import scala.collection.immutable.Seq 4 | import jakarta.inject.Inject 5 | import jakarta.inject.Provider 6 | import jakarta.inject.Singleton 7 | 8 | import play.api.Configuration 9 | import play.api.Environment 10 | import play.api.Mode 11 | import play.api.PlayException 12 | import play.api.inject.ApplicationLifecycle 13 | import play.api.inject.Binding 14 | import play.api.inject.BindingKey 15 | import play.api.inject.Module 16 | import play.api.libs.Files 17 | import play.db.NamedDatabaseImpl 18 | import slick.basic.DatabaseConfig 19 | import slick.basic.BasicProfile 20 | 21 | import scala.concurrent.ExecutionContext 22 | 23 | object SlickModule { 24 | 25 | /** path in the **reference.conf** to obtain the path under which databases are configured. */ 26 | final val DbKeyConfig = "play.slick.db.config" 27 | 28 | /** path in the **reference.conf** to obtain the name of the default database. */ 29 | final val DefaultDbName = "play.slick.db.default" 30 | } 31 | 32 | @Singleton 33 | final class SlickModule extends Module { 34 | def bindings(environment: Environment, configuration: Configuration): Seq[Binding[?]] = { 35 | val config = configuration.underlying 36 | val dbKey = config.getString(SlickModule.DbKeyConfig) 37 | val default = config.getString(SlickModule.DefaultDbName) 38 | val dbs = configuration.getOptional[Configuration](dbKey).getOrElse(Configuration.empty).subKeys 39 | Seq(bind[SlickApi].to[DefaultSlickApi].in[Singleton]) ++ namedDatabaseConfigBindings( 40 | dbs 41 | ) ++ defaultDatabaseConfigBinding( 42 | default, 43 | dbs 44 | ) 45 | } 46 | 47 | def namedDatabaseConfigBindings(dbs: Set[String]): Seq[Binding[?]] = dbs.toList.map { db => 48 | bindNamed(db).to(new NamedDatabaseConfigProvider(db)) 49 | } 50 | 51 | def defaultDatabaseConfigBinding(default: String, dbs: Set[String]): Seq[Binding[?]] = 52 | if (dbs.contains(default)) Seq(bind[DatabaseConfigProvider].to(bindNamed(default))) else Nil 53 | 54 | def bindNamed(name: String): BindingKey[DatabaseConfigProvider] = 55 | bind[DatabaseConfigProvider].qualifiedWith(new NamedDatabaseImpl(name)) 56 | } 57 | 58 | /** Inject provider for named databases. */ 59 | final class NamedDatabaseConfigProvider(name: String) extends Provider[DatabaseConfigProvider] { 60 | @Inject private var slickApi: SlickApi = _ 61 | 62 | lazy val get: DatabaseConfigProvider = new DatabaseConfigProvider { 63 | def get[P <: BasicProfile]: DatabaseConfig[P] = slickApi.dbConfig[P](DbName(name)) 64 | } 65 | } 66 | 67 | trait SlickComponents { 68 | def environment: Environment 69 | def configuration: Configuration 70 | def applicationLifecycle: ApplicationLifecycle 71 | def executionContext: ExecutionContext 72 | 73 | lazy val slickApi: SlickApi = new DefaultSlickApi(environment, configuration, applicationLifecycle)( 74 | using executionContext 75 | ) 76 | } 77 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/PlaySlickAdvancedTopics.md: -------------------------------------------------------------------------------- 1 | # Play Slick Advanced Topics 2 | 3 | ## Connection Pool 4 | 5 | With Slick 3 release, Slick starts and controls both a connection pool and a thread pool for optimal asynchronous execution of your database actions. 6 | 7 | In Play Slick we have decided to let Slick be in control of creating and managing the connection pool (the default connection pool used by Slick 3 is [HikariCP](https://github.com/brettwooldridge/HikariCP)), which means that to tune the connection pool you will need to look at the Slick ScalaDoc for [Database.forConfig](https://scala-slick.org/doc/3.3.2/api/index.html#slick.jdbc.JdbcBackend$DatabaseFactoryDef@forConfig%28path:String,config:com.typesafe.config.Config,driver:java.sql.Driver,classLoader:ClassLoader%29:JdbcBackend.this.Database) (make sure to expand the `forConfig` row in the doc). In fact, be aware that any value you may pass for setting the Play connection pool (e.g., under the key `play.db.default.hikaricp`) is simply not picked up by Slick, and hence effectively ignored. 8 | 9 | Also, note that as stated in the [Slick documentation](https://scala-slick.org/doc/3.3.2/database.html#connection-pools), a reasonable default for the connection pool size is calculated from the thread pool size. In fact, you should only need to tune `numThreads` and `queueSize` in most cases, for each of your database configurations. 10 | 11 | Finally, it's worth mentioning that while Slick allows using a different connection pool than [HikariCP](https://github.com/brettwooldridge/HikariCP) (though, Slick currently only offers built-in support for HikariCP, and requires you to provide an implementation of [JdbcDataSourceFactory](https://scala-slick.org/doc/3.3.2/api/index.html#slick.jdbc.JdbcDataSourceFactory) if you want to use a different connection pool), Play Slick currently doesn't allow using a different connection pool than HikariCP. 12 | 13 | > **Note**: Changing the value of `play.db.pool` won't affect what connection pool Slick is using. Furthermore, be aware that any configuration under `play.db` is not considered by Play Slick. 14 | 15 | ## Thread Pool 16 | 17 | With Slick 3.0 release, Slick starts and controls both a thread pool and a connection pool for optimal asynchronous execution of your database actions. 18 | 19 | For optimal execution, you may need to tune the `numThreads` and `queueSize` parameters, for each of your database configuration. Refer to the [Slick documentation](https://scala-slick.org/doc/3.3.2/database.html#database-thread-pool) for details. 20 | 21 | Since `3.2.2` of Slick, there is a requirement that `maxConnections = maxThreads` otherwise you might see these messages in your logs: 22 | 23 | > [warn] s.u.AsyncExecutor - Having maxConnection > maxThreads can result in deadlocks if transactions or database locks are used. 24 | 25 | To fix this, simply set them in your config. For example, 26 | 27 | ``` 28 | slick.dbs.default.profile="slick.profile.PostgresProfile$" 29 | slick.dbs.default.db.dataSourceClass="slick.jdbc.DatabaseUrlDataSource" 30 | slick.dbs.default.db.numThreads=20 31 | slick.dbs.default.db.maxConnections=20 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/PlaySlickFAQ.md: -------------------------------------------------------------------------------- 1 | # Play Slick FAQ 2 | 3 | ## What version should I use? 4 | 5 | Have a look at the [compatibility matrix](https://github.com/playframework/play-slick#all-releases) to know what version you should be using. 6 | 7 | ## `play.db.pool` is ignored 8 | 9 | It's indeed the case. Changing the value of `play.db.pool` won't affect what connection pool Slick is going to use. The reason is simply that the Play Slick module currently doesn't support using a different connection pool than [HikariCP](https://github.com/brettwooldridge/HikariCP). 10 | 11 | ## Changing the connection pool used by Slick 12 | 13 | While Slick allows using a different connection pool than [HikariCP](https://github.com/brettwooldridge/HikariCP) (though, Slick currently only offers built-in support for HikariCP, and requires you to provide an implementation of [JdbcDataSourceFactory](https://scala-slick.org/doc/3.3.2/api/index.html#slick.jdbc.JdbcDataSourceFactory) if you want to use a different connection pool), Play Slick currently doesn't allow using a different connection pool than HikariCP. If you find yourself needing this feature, you can try to drop us a note in the [Play Discussion Forum](https://github.com/playframework/playframework/discussions). 14 | 15 | ## A binding to `play.api.db.DBApi` was already configured 16 | 17 | If you get the following exception when starting your Play application: 18 | 19 | ``` 20 | 1) A binding to play.api.db.DBApi was already configured at play.api.db.slick.evolutions.EvolutionsModule.bindings: 21 | Binding(interface play.api.db.DBApi to ConstructionTarget(class play.api.db.slick.evolutions.internal.DBApiAdapter) in interface jakarta.inject.Singleton). 22 | at play.api.db.DBModule.bindings(DBModule.scala:25): 23 | Binding(interface play.api.db.DBApi to ProviderConstructionTarget(class play.api.db.DBApiProvider)) 24 | ``` 25 | 26 | It is very likely that you have [[enabled the jdbc plugin|AccessingAnSQLDatabase]], and that doesn't really make sense if you are using Slick for accessing your databases. To fix the issue simply remove the Play *jdbc* component from your project's build. 27 | 28 | Another possibility is that there is another Play module that is binding [DBApi](api/scala/play/api/db/DBApi.html) to some other concrete implementation. This means that you are still trying to use Play Slick together with another Play module for database access, which is likely not what you want. 29 | 30 | ## Play throws `java.lang.ClassNotFoundException: org.h2.tools.Server` 31 | 32 | If you get the following exception when starting your Play application: 33 | 34 | ``` 35 | java.lang.ClassNotFoundException: org.h2.tools.Server 36 | at java.net.URLClassLoader$1.run(URLClassLoader.java:372) 37 | at java.net.URLClassLoader$1.run(URLClassLoader.java:361) 38 | ... 39 | ``` 40 | 41 | It means you are trying to use a H2 database, but have forgot to add a dependency to it in your project's build. Fixing the problem is simple, just add the missing dependency in your project's build, e.g., 42 | 43 | ```scala 44 | "com.h2database" % "h2" % "${H2_VERSION}" // replace `${H2_VERSION}` with an actual version number 45 | ``` 46 | -------------------------------------------------------------------------------- /src/core/src/test/scala/play/api/db/slick/SlickModuleSpec.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import org.specs2.matcher.ValueCheck.typedValueCheck 4 | import org.specs2.mutable.Specification 5 | 6 | import play.api.db.slick.util.WithReferenceConfig 7 | import play.api.inject.BindingKey 8 | import play.api.inject.QualifierInstance 9 | import play.api.inject.guice.GuiceApplicationBuilder 10 | import play.db.NamedDatabaseImpl 11 | 12 | class SlickModuleSpec extends Specification { 13 | "reference.conf" should { 14 | "slick module is enabled" in new WithReferenceConfig { 15 | enabledModules(ref) must contain(classOf[SlickModule].getName) 16 | } 17 | "provide a database config default path" in new WithReferenceConfig { 18 | val dbsKey = ref.getOptional[String](SlickModule.DbKeyConfig) 19 | dbsKey must beSome("slick.dbs") 20 | } 21 | 22 | "provide a name for the default database" in new WithReferenceConfig { 23 | val dbsKey = ref.getOptional[String](SlickModule.DefaultDbName) 24 | dbsKey must beSome("default") 25 | } 26 | } 27 | 28 | "SlickModule" should { 29 | val appBuilder = GuiceApplicationBuilder(configuration = TestData.configuration) 30 | val injector = appBuilder.injector() 31 | 32 | "bind SlickApi to DefaultSlickApi" in { 33 | val api = injector.instanceOf[SlickApi] 34 | api must beAnInstanceOf[DefaultSlickApi] 35 | } 36 | "bind SlickApi as a singleton" in { 37 | val api1 = injector.instanceOf[SlickApi] 38 | val api2 = injector.instanceOf[SlickApi] 39 | api1 mustEqual api2 40 | } 41 | "bind the default database to a DatabaseConfigProvider" in { 42 | val dbConfProvider = injector.instanceOf[DatabaseConfigProvider] 43 | dbConfProvider must not(beNull) 44 | } 45 | "return a DatabaseConfigProvider with a DatabaseConfig instance for the default database" in { 46 | val dbConfProvider = injector.instanceOf[DatabaseConfigProvider] 47 | dbConfProvider.get must not(beNull) 48 | } 49 | "return the same DatabaseConfig instance for the default database" in { 50 | val dbConfProvider1 = injector.instanceOf[DatabaseConfigProvider] 51 | val dbConfProvider2 = injector.instanceOf[DatabaseConfigProvider] 52 | dbConfProvider1.get mustEqual dbConfProvider2.get 53 | } 54 | "return a DatabaseConfigProvider with a DatabaseConfig instance for the database named default" in { 55 | val binding = 56 | new BindingKey(classOf[DatabaseConfigProvider], Some(QualifierInstance(new NamedDatabaseImpl("default")))) 57 | val dbConfProvider = injector.instanceOf(binding) 58 | dbConfProvider.get must not(beNull) 59 | } 60 | "return the same DatabaseConfig instance for the database named default" in { 61 | val binding = 62 | new BindingKey(classOf[DatabaseConfigProvider], Some(QualifierInstance(new NamedDatabaseImpl("default")))) 63 | val dbConfProvider1 = injector.instanceOf(binding) 64 | val dbConfProvider2 = injector.instanceOf(binding) 65 | dbConfProvider1.get mustEqual dbConfProvider2.get 66 | } 67 | "return a DatabaseConfigProvider with a DatabaseConfig instance for a named (not default) database" in { 68 | val binding = 69 | new BindingKey(classOf[DatabaseConfigProvider], Some(QualifierInstance(new NamedDatabaseImpl("somedb")))) 70 | val dbConfProvider = injector.instanceOf(binding) 71 | dbConfProvider.get must not(beNull) 72 | } 73 | "return the same DatabaseConfig instance for a named database" in { 74 | val binding = 75 | new BindingKey(classOf[DatabaseConfigProvider], Some(QualifierInstance(new NamedDatabaseImpl("somedb")))) 76 | val dbConfProvider1 = injector.instanceOf(binding) 77 | val dbConfProvider2 = injector.instanceOf(binding) 78 | dbConfProvider1.get mustEqual dbConfProvider2.get 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/core/src/main/scala/play/api/db/slick/SlickApi.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import scala.collection.immutable.Seq 4 | import scala.concurrent.ExecutionContext 5 | import scala.concurrent.Future 6 | import scala.util.Failure 7 | import scala.util.Success 8 | import scala.util.Try 9 | import scala.util.control.NonFatal 10 | import com.typesafe.config.Config 11 | import com.typesafe.config.ConfigValue 12 | import com.typesafe.config.ConfigValueFactory 13 | 14 | import jakarta.inject.Inject 15 | import play.api.Configuration 16 | import play.api.Environment 17 | import play.api.Logger 18 | import play.api.PlayException 19 | import play.api.inject.ApplicationLifecycle 20 | import slick.basic.DatabaseConfig 21 | import slick.basic.BasicProfile 22 | 23 | trait SlickApi { 24 | 25 | /** 26 | * Returns all database configs, for all databases defined in the loaded application's configuration. 27 | * @throws PlayException 28 | * if a database config cannot be created. 29 | */ 30 | @throws(classOf[PlayException]) 31 | def dbConfigs[P <: BasicProfile](): Seq[(DbName, DatabaseConfig[P])] 32 | 33 | /** 34 | * Returns a database config instance for the database named `name` in the loaded application's configuration. 35 | * @throws PlayException 36 | * if a database config for the passed `name` cannot be created. 37 | */ 38 | @throws(classOf[PlayException]) 39 | def dbConfig[P <: BasicProfile](name: DbName): DatabaseConfig[P] 40 | } 41 | 42 | final class DefaultSlickApi @Inject() ( 43 | environment: Environment, 44 | configuration: Configuration, 45 | lifecycle: ApplicationLifecycle 46 | )(implicit executionContext: ExecutionContext) 47 | extends SlickApi { 48 | import DefaultSlickApi.DatabaseConfigFactory 49 | 50 | private lazy val dbconfigFactoryByName: Map[DbName, DatabaseConfigFactory] = { 51 | def configs: Map[String, Config] = { 52 | val config = configuration.underlying 53 | val slickDbKey = config.getString(SlickModule.DbKeyConfig) 54 | if (config.hasPath(slickDbKey)) { 55 | val playConfig = Configuration(config) 56 | playConfig.get[Map[String, Config]](slickDbKey) 57 | } else Map.empty[String, Config] 58 | } 59 | (for ((name, config) <- configs) yield (DbName(name), new DatabaseConfigFactory(name, config, lifecycle))).toMap 60 | } 61 | 62 | // Be careful that accessing this field will trigger initialization of ALL database configs! 63 | private lazy val allDbConfigs: List[(DbName, DatabaseConfig[BasicProfile])] = 64 | dbconfigFactoryByName.map { case (name, factory) => (name, factory.get) }.toList 65 | 66 | def dbConfigs[P <: BasicProfile](): Seq[(DbName, DatabaseConfig[P])] = 67 | allDbConfigs.asInstanceOf[Seq[(DbName, DatabaseConfig[P])]] 68 | 69 | def dbConfig[P <: BasicProfile](name: DbName): DatabaseConfig[P] = { 70 | val factory: DatabaseConfigFactory = dbconfigFactoryByName.getOrElse( 71 | name, 72 | throw new PlayException(s"No database configuration found for ", name.value) 73 | ) 74 | val dbConf: DatabaseConfig[BasicProfile] = factory.get 75 | dbConf.asInstanceOf[DatabaseConfig[P]] 76 | } 77 | } 78 | 79 | object DefaultSlickApi { 80 | private object DatabaseConfigFactory { 81 | private val logger = Logger(classOf[DefaultSlickApi]) 82 | } 83 | // This class is useful for delaying the creation of `DatabaseConfig` instances. 84 | private class DatabaseConfigFactory(name: String, config: Config, lifecycle: ApplicationLifecycle)(implicit 85 | executionContext: ExecutionContext 86 | ) { 87 | import DatabaseConfigFactory.logger 88 | 89 | @throws(classOf[PlayException]) 90 | lazy val get: DatabaseConfig[BasicProfile] = { 91 | val dbConf = create() 92 | logger.debug(s"Created Slick database config for key $name.") 93 | registerDatabaseShutdownHook(dbConf) 94 | dbConf 95 | } 96 | 97 | @throws(classOf[PlayException]) 98 | private def create(): DatabaseConfig[BasicProfile] = { 99 | val config2 = 100 | if (config.hasPath("db.poolName")) config 101 | else 102 | config.withValue("db.poolName", ConfigValueFactory.fromAnyRef(s"$name.db")) 103 | try DatabaseConfig.forConfig[BasicProfile](path = "", config = config2) 104 | catch { 105 | case NonFatal(t) => 106 | logger.error(s"Failed to create Slick database config for key $name.", t) 107 | throw Configuration(config2).reportError(name, s"Cannot connect to database [$name]", Some(t)) 108 | } 109 | } 110 | 111 | private def registerDatabaseShutdownHook(dbConf: DatabaseConfig[?]): Unit = { 112 | // clean-up when the application is stopped. 113 | lifecycle.addStopHook { () => 114 | Future { 115 | Try(dbConf.db.close()) match { 116 | case Success(_) => logger.debug(s"Database $name was successfully closed.") 117 | case Failure(t) => logger.warn(s"Error occurred while closing database $name.", t) 118 | } 119 | } 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/evolutions/src/main/scala/play/api/db/slick/evolutions/internal/DBApiAdapter.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick.evolutions.internal 2 | 3 | import java.sql.Connection 4 | 5 | import jakarta.inject.Inject 6 | import javax.sql.DataSource 7 | import play.api.Logger 8 | import play.api.db.slick.DbName 9 | import play.api.db.slick.IssueTracker 10 | import play.api.db.slick.SlickApi 11 | import play.api.db.DBApi 12 | import play.api.db.TransactionIsolationLevel 13 | import play.api.db.{ Database => PlayDatabase } 14 | import slick.basic.DatabaseConfig 15 | import slick.jdbc.DataSourceJdbcDataSource 16 | import slick.jdbc.JdbcProfile 17 | import slick.jdbc.hikaricp.HikariCPJdbcDataSource 18 | 19 | import scala.util.control.ControlThrowable 20 | 21 | private[evolutions] class DBApiAdapter @Inject() (slickApi: SlickApi) extends DBApi { 22 | private lazy val databasesByName: Map[DbName, PlayDatabase] = slickApi 23 | .dbConfigs[JdbcProfile]() 24 | .map { case (name, dbConfig) => 25 | (name, new DBApiAdapter.DatabaseAdapter(name, dbConfig)) 26 | } 27 | .toMap 28 | 29 | override def databases(): Seq[PlayDatabase] = databasesByName.values.toSeq 30 | 31 | def database(name: String): PlayDatabase = 32 | databasesByName.getOrElse(DbName(name), throw new IllegalArgumentException(s"Could not find database for $name")) 33 | 34 | def shutdown(): Unit = { 35 | // no-op: shutting down dbs is automatically managed by `slickApi` 36 | () 37 | } 38 | } 39 | 40 | private[evolutions] object DBApiAdapter { 41 | // I don't really like this adapter as it can be used as a trojan horse. Let's keep things simple for the moment, 42 | // but in the future we may need to become more defensive and provide custom implementation for `java.sql.Connection` 43 | // and `java.sql.DataSource` to prevent the ability of closing a database connection or database when using this 44 | // adapter class. 45 | private class DatabaseAdapter(_name: DbName, dbConfig: DatabaseConfig[JdbcProfile]) extends PlayDatabase { 46 | private val logger = Logger(classOf[DatabaseAdapter]) 47 | 48 | def name: String = _name.value 49 | 50 | def dataSource: DataSource = { 51 | dbConfig.db.source match { 52 | case ds: DataSourceJdbcDataSource => ds.ds 53 | case hds: HikariCPJdbcDataSource => hds.ds 54 | case other => 55 | logger.error(s"Unexpected data source type ${other.getClass}. Please, file a ticket $IssueTracker.") 56 | throw new UnsupportedOperationException 57 | } 58 | } 59 | 60 | lazy val url: String = withConnection(_.getMetaData.getURL()) 61 | 62 | override def getConnection(): Connection = getConnection(autocommit = true) 63 | 64 | override def getConnection(autocommit: Boolean): Connection = { 65 | val connection = dbConfig.db.source.createConnection() 66 | try { 67 | connection.setAutoCommit(autocommit) 68 | } catch { 69 | case e: Throwable => 70 | // setAutoCommit can fail, so we need to close the connection 71 | // which usually means we are returning it back to the pool and 72 | // avoiding a leak. 73 | connection.close() 74 | throw e 75 | } 76 | connection 77 | } 78 | 79 | def withConnection[A](block: Connection => A): A = { 80 | withConnection(autocommit = true)(block) 81 | } 82 | 83 | def withConnection[A](autocommit: Boolean)(block: Connection => A): A = { 84 | val connection = getConnection(autocommit) 85 | try { 86 | block(connection) 87 | } finally { 88 | connection.close() 89 | } 90 | } 91 | 92 | def withTransaction[A](block: Connection => A): A = { 93 | withConnection(autocommit = false) { connection => 94 | try { 95 | val r = block(connection) 96 | connection.commit() 97 | r 98 | } catch { 99 | case e: ControlThrowable => 100 | connection.commit() 101 | throw e 102 | case e: Throwable => 103 | connection.rollback() 104 | throw e 105 | } 106 | } 107 | } 108 | 109 | override def withTransaction[A](isolationLevel: TransactionIsolationLevel)(block: Connection => A): A = { 110 | withConnection(autocommit = false) { connection => 111 | val oldIsolationLevel = connection.getTransactionIsolation 112 | try { 113 | connection.setTransactionIsolation(isolationLevel.id) 114 | val r = block(connection) 115 | connection.commit() 116 | r 117 | } catch { 118 | case e: ControlThrowable => 119 | connection.commit() 120 | throw e 121 | case e: Throwable => 122 | connection.rollback() 123 | throw e 124 | } finally { 125 | connection.setTransactionIsolation(oldIsolationLevel) 126 | } 127 | } 128 | } 129 | 130 | def shutdown(): Unit = { 131 | // no-op. The rationale is that play-slick already takes care of closing the database on application shutdown 132 | () 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Play Slick 2 | 3 | [![Twitter Follow](https://img.shields.io/twitter/follow/playframework?label=follow&style=flat&logo=twitter&color=brightgreen)](https://twitter.com/playframework) 4 | [![Discord](https://img.shields.io/discord/931647755942776882?logo=discord&logoColor=white)](https://discord.gg/g5s2vtZ4Fa) 5 | [![GitHub Discussions](https://img.shields.io/github/discussions/playframework/playframework?&logo=github&color=brightgreen)](https://github.com/playframework/playframework/discussions) 6 | [![StackOverflow](https://img.shields.io/static/v1?label=stackoverflow&logo=stackoverflow&logoColor=fe7a16&color=brightgreen&message=playframework)](https://stackoverflow.com/tags/playframework) 7 | [![YouTube](https://img.shields.io/youtube/channel/views/UCRp6QDm5SDjbIuisUpxV9cg?label=watch&logo=youtube&style=flat&color=brightgreen&logoColor=ff0000)](https://www.youtube.com/channel/UCRp6QDm5SDjbIuisUpxV9cg) 8 | [![Twitch Status](https://img.shields.io/twitch/status/playframework?logo=twitch&logoColor=white&color=brightgreen&label=live%20stream)](https://www.twitch.tv/playframework) 9 | [![OpenCollective](https://img.shields.io/opencollective/all/playframework?label=financial%20contributors&logo=open-collective)](https://opencollective.com/playframework) 10 | 11 | [![Build Status](https://github.com/playframework/play-slick/actions/workflows/build-test.yml/badge.svg)](https://github.com/playframework/play-slick/actions/workflows/build-test.yml) 12 | [![Maven](https://img.shields.io/maven-central/v/org.playframework/play-slick_2.13.svg?logo=apache-maven)](https://mvnrepository.com/artifact/org.playframework/play-slick_2.13) 13 | [![Repository size](https://img.shields.io/github/repo-size/playframework/play-slick.svg?logo=git)](https://github.com/playframework/play-slick) 14 | [![Scala Steward badge](https://img.shields.io/badge/Scala_Steward-helping-blue.svg?style=flat&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAQCAMAAAARSr4IAAAAVFBMVEUAAACHjojlOy5NWlrKzcYRKjGFjIbp293YycuLa3pYY2LSqql4f3pCUFTgSjNodYRmcXUsPD/NTTbjRS+2jomhgnzNc223cGvZS0HaSD0XLjbaSjElhIr+AAAAAXRSTlMAQObYZgAAAHlJREFUCNdNyosOwyAIhWHAQS1Vt7a77/3fcxxdmv0xwmckutAR1nkm4ggbyEcg/wWmlGLDAA3oL50xi6fk5ffZ3E2E3QfZDCcCN2YtbEWZt+Drc6u6rlqv7Uk0LdKqqr5rk2UCRXOk0vmQKGfc94nOJyQjouF9H/wCc9gECEYfONoAAAAASUVORK5CYII=)](https://scala-steward.org) 15 | [![Mergify Status](https://img.shields.io/endpoint.svg?url=https://api.mergify.com/v1/badges/playframework/play-slick&style=flat)](https://mergify.com) 16 | 17 | The Play Slick module makes [Slick] a first-class citizen of [Play]. It consists of two features: 18 | 19 | - Integration of Slick into Play's application lifecycle. 20 | - Support for Play database evolutions. 21 | 22 | Because Slick creates and manages both a connection pool and a thread pool, integrating Slick with Play boils down to ensuring that all resources allocated by Slick are shutdown when a Play application is stopped (or reloaded). 23 | 24 | [Play]: https://www.playframework.com 25 | [Slick]: https://scala-slick.org/ 26 | 27 | The plugin has its own release cycle and therefore is not integrated into either core Play or Slick. 28 | 29 | Examples of `play-slick`s usage can be found [here](https://github.com/playframework/play-samples). 30 | 31 | ## Current Version 32 | 33 | To use play-slick, you need to add the following dependencies: 34 | 35 | ```scala 36 | libraryDependencies ++= Seq( 37 | "org.playframework" %% "play-slick" % "6.2.0", 38 | "org.playframework" %% "play-slick-evolutions" % "6.2.0" 39 | ) 40 | ``` 41 | 42 | Or use a version that is compatible with the Play version you are using. See all available versions below. 43 | 44 | ## All Releases 45 | 46 | The Play Slick plugin supports several different versions of Play and Slick. 47 | 48 | | Plugin version | Play version | Slick version | Scala version | 49 | |----------------|--------------|---------------|----------------------| 50 | | 6.2.x | 3.0.0 | 3.6.0+ | 2.13.x/3.3.x | 51 | | 5.4.x | 2.9.0 | 3.6.0+ | 2.13.x/3.3.x | 52 | | 6.1.x | 3.0.0 | 3.5.0+ | 2.13.x/3.3.x | 53 | | 5.3.x | 2.9.0 | 3.5.0+ | 2.13.x/3.3.x | 54 | | 6.0.x | 3.0.0 | 3.4.1 | 2.13.x | 55 | | 5.2.x | 2.9.0 | 3.4.1 | 2.13.x | 56 | | 5.1.x | 2.8.16 | 3.4.1+ | 2.12.x/2.13.x | 57 | | 5.0.x | 2.8.x | 3.3.2+ | 2.12.x/2.13.x | 58 | | 4.0.2+ | 2.7.x | 3.3.2+ | 2.11.x/2.12.x/2.13.x | 59 | | 4.0.x | 2.7.x | 3.3.x | 2.11.x/2.12.x | 60 | | 3.0.x | 2.6.x | 3.2.x | 2.11.x/2.12.x | 61 | | 2.1.x | 2.5.x | 3.2.0 | 2.11.x | 62 | | 2.0.x | 2.5.x | 3.1.0 | 2.11.x | 63 | | 1.1.x | 2.4.x | 3.1.0 | 2.10.x/2.11.x | 64 | | 1.0.1 | 2.4.x | 3.0.1 | 2.10.x/2.11.x | 65 | | 1.0.0 | 2.4.x | 3.0.0 | 2.10.x/2.11.x | 66 | | 0.8.x | 2.3.x | 2.1.0 | 2.10.x/2.11.x | 67 | | 0.7.0 | 2.3.x | 2.0.2 | 2.10.x | 68 | | 0.6.1 | 2.2.x | 2.0.x | 2.10.x | 69 | | 0.5.1 | 2.2.x | 1.0.x | 2.10.x | 70 | 71 | > * Release Candidate: these releases are not stable and should not be used in production. 72 | 73 | Note that the `+` next to a version means that the specified version and later trailing point releases are supported by the same version of the play-slick plugin. While a `x` means that any trailing point release is supported by the same version of play-slick. 74 | 75 | ## Documentation 76 | 77 | The documentation for the latest release is available [here](https://www.playframework.com/documentation/latest/PlaySlick). 78 | 79 | ## Releasing a new version 80 | 81 | See https://github.com/playframework/.github/blob/main/RELEASING.md 82 | 83 | ## Copyright 84 | 85 | License: Apache License 2.0, 86 | -------------------------------------------------------------------------------- /src/core/src/main/scala/play/api/db/slick/DatabaseConfigProvider.scala: -------------------------------------------------------------------------------- 1 | package play.api.db.slick 2 | 3 | import slick.basic.BasicProfile 4 | import slick.basic.DatabaseConfig 5 | 6 | /** 7 | * Generic interface for a provider of a `DatabaseConfig` instance. A `DatabaseConfig` is Slick type that bundles a 8 | * database and profile. 9 | * 10 | * Usually, you shouldn't need to create instances of `DatabaseConfigProvider` explicitly. Rather, you should rely on 11 | * dependency injection. If you don't want to use dependency injection, then use the companion object and call 12 | * `DatabaseConfigProvider.get`. 13 | * 14 | * ==Example== 15 | * 16 | * Here is an example of how you can use dependency injection to obtain an instance of `DatabaseConfigProvider`, for the 17 | * database named `default` in your **application.conf**. 18 | * {{{ 19 | * class Application @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) { 20 | * // ... 21 | * } 22 | * }}} 23 | * 24 | * While here is an example for injecting a `DatabaseConfigProvider` for a database named `orders` in your 25 | * **application.conf**. 26 | * {{{ 27 | * import play.db.NamedDatabase 28 | * class Application @Inject()(@NamedDatabase("orders") protected val dbConfigProvider: DatabaseConfigProvider) { 29 | * // ... 30 | * } 31 | * }}} 32 | */ 33 | trait DatabaseConfigProvider { 34 | def get[P <: BasicProfile]: DatabaseConfig[P] 35 | } 36 | 37 | /** 38 | * Look up a `DatabaseConfig` (which is Slick type that bundles a database and profile) for the passed database name. 39 | * The `DatabaseConfig` instance is created using the database's configuration you have provided in your 40 | * **application.conf**, for the passed database name. 41 | * 42 | * Note that if no database name is passed, `default` is used, and hence the configuration `slick.dbs.default` is used 43 | * to create the `DatabaseConfig` instance. 44 | * 45 | * ==Example== 46 | * 47 | * Here is an example for obtaining a `DatabaseConfig` instance for the database named `default` in your 48 | * **application.conf**. 49 | * {{{ 50 | * import play.api.Play 51 | * import play.api.db.slick.DatabaseConfigProvider 52 | * import slick.profile.RelationalProfile 53 | * val dbConfig = DatabaseConfigProvider.get[RelationalProfile](Play.current) 54 | * }}} 55 | * 56 | * While here is an example for obtaining a `DatabaseConfig` instance for the database named `orders` in your 57 | * **application.conf**. 58 | * {{{ 59 | * import play.api.Play 60 | * import play.api.db.slick.DatabaseConfigProvider 61 | * import slick.profile.RelationalProfile 62 | * val dbConfig = DatabaseConfigProvider.get[RelationalProfile]("orders")(Play.current) 63 | * }}} 64 | */ 65 | object DatabaseConfigProvider { 66 | import play.api.Application 67 | private object DatabaseConfigLocator { 68 | import play.api.Configuration 69 | private val slickApiCache = Application.instanceCache[SlickApi] 70 | private def slickApi(implicit app: Application): SlickApi = slickApiCache(app) 71 | 72 | private val configurationCache = Application.instanceCache[Configuration] 73 | private def configuration(implicit app: Application): Configuration = configurationCache(app) 74 | 75 | @throws(classOf[IllegalArgumentException]) 76 | def apply[P <: BasicProfile](implicit app: Application): DatabaseConfig[P] = { 77 | val defaultDbName = configuration.underlying.getString(SlickModule.DefaultDbName) 78 | this(defaultDbName) 79 | } 80 | 81 | @throws(classOf[IllegalArgumentException]) 82 | def apply[P <: BasicProfile](dbName: String)(implicit app: Application): DatabaseConfig[P] = 83 | slickApi.dbConfig[P](DbName(dbName)) 84 | } 85 | 86 | /** 87 | * Returns a Slick database config for the `default` database declared in your **application.conf**. Throws a 88 | * IllegalArgumentException if your **application.conf** does not contain a configuration for the `default` database. 89 | * 90 | * @return 91 | * a Slick `DatabaseConfig` instance for the `default` database. 92 | */ 93 | @throws(classOf[IllegalArgumentException]) 94 | @deprecated( 95 | "Use DatabaseConfigProvider#get[P] or SlickApi#dbConfig[P](\"default\") on injected instances", 96 | "3.0.0" 97 | ) 98 | def get[P <: BasicProfile](implicit app: Application): DatabaseConfig[P] = 99 | DatabaseConfigLocator(using app) 100 | 101 | /** 102 | * Returns a Slick database config for the passed `dbName`. Throws a IllegalArgumentException if no database 103 | * configuration exist in your **application.conf** for the passed `dbName`. 104 | * 105 | * @param dbName 106 | * the name of a database in your **application.conf**. 107 | * @return 108 | * a Slick `DatabaseConfig` instance for the requested database name. 109 | */ 110 | @throws(classOf[IllegalArgumentException]) 111 | @deprecated( 112 | "Inject DatabaseConfigProvider using @Named(\"dbName\") and call get[P] or use SlickApi#dbConfig[P](name)", 113 | "3.0.0" 114 | ) 115 | def get[P <: BasicProfile](dbName: String)(implicit app: Application): DatabaseConfig[P] = 116 | DatabaseConfigLocator(dbName) 117 | } 118 | 119 | /** 120 | * Mix-in this trait if you need a Slick database and profile. This is useful if you need to define a Slick table or 121 | * need to execute some operation in the database. 122 | * 123 | * There is only one abstract field, `dbConfig`, which you can implement by calling `DatabaseConfigProvider.get`. If you 124 | * are injecting `DatabaseConfigProvider` instances using dependency injection, prefer using the trait 125 | * `HasDatabaseConfigProvider` instead of this one. 126 | * 127 | * ==Example== 128 | * 129 | * {{{ 130 | * // model definition 131 | * class Cat(name: String, color: String) 132 | * // DAO definition 133 | * class CatDAO extends HasDatabaseConfig[RelationalProfile] { 134 | * protected val dbConfig = DatabaseConfigProvider.get[RelationalProfile](Play.current) 135 | * import profile.api._ 136 | * 137 | * private val Cats = TableQuery[CatsTable] 138 | * def all() = db.run(Cats.result) 139 | * def insert(cat: Cat) = db.run(Cats += cat) 140 | * 141 | * // Slick table definition 142 | * private class CatsTable(tag: Tag) extends Table[Cat](tag, "CAT") { 143 | * def name = column[String]("NAME", O.PrimaryKey) 144 | * def color = column[String]("COLOR") 145 | * def * = (name, color) <> (Cat.tupled, Cat.unapply _) 146 | * } 147 | * } 148 | * }}} 149 | * 150 | * Of course, you do not need to define a DAO to use this trait (the above it is really just an example of usage). 151 | */ 152 | trait HasDatabaseConfig[P <: BasicProfile] { 153 | 154 | /** The Slick database configuration. */ 155 | protected lazy val dbConfig: DatabaseConfig[P] = 156 | ??? // field is declared as a val because we want a stable identifier. // TODO: Remove "= ???" when dropping Scala 2 157 | /** The Slick profile extracted from `dbConfig`. */ 158 | protected final lazy val profile: P = dbConfig.profile // field is lazy to avoid early initializer problems. 159 | @deprecated("Use `profile` instead of `driver`", "2.1") 160 | protected final lazy val driver: P = dbConfig.profile // field is lazy to avoid early initializer problems. 161 | /** The Slick database extracted from `dbConfig`. */ 162 | protected final def db = dbConfig.db 163 | } 164 | 165 | /** 166 | * Mix-in this trait if you need a Slick database and profile, and you are using dependency injection for obtaining an 167 | * instance of `DatabaseConfigProvider`. If you are not using dependency injection, then prefer mixing 168 | * `HasDatabaseConfig` instead. 169 | * 170 | * This trait is useful if you need to define a Slick table or need to execute some operation in the database. 171 | * 172 | * ==Example== 173 | * 174 | * {{{ 175 | * // model definition 176 | * class Cat(name: String, color: String) 177 | * // DAO definition 178 | * class CatDAO @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[RelationalProfile] { 179 | * import profile.api._ 180 | * 181 | * private val Cats = TableQuery[CatsTable] 182 | * def all() = db.run(Cats.result) 183 | * def insert(cat: Cat) = db.run(Cats += cat) 184 | * 185 | * // Slick table definition 186 | * private class CatsTable(tag: Tag) extends Table[Cat](tag, "CAT") { 187 | * def name = column[String]("NAME", O.PrimaryKey) 188 | * def color = column[String]("COLOR") 189 | * def * = (name, color) <> (Cat.tupled, Cat.unapply _) 190 | * } 191 | * } 192 | * }}} 193 | * 194 | * Of course, you do not need to define a DAO to use this trait (the above it is really just an example of usage). 195 | */ 196 | trait HasDatabaseConfigProvider[P <: BasicProfile] extends HasDatabaseConfig[P] { 197 | 198 | /** The provider of a Slick `DatabaseConfig` instance. */ 199 | protected val dbConfigProvider: DatabaseConfigProvider 200 | protected final override lazy val dbConfig: DatabaseConfig[P] = 201 | dbConfigProvider.get[P] // field is lazy to avoid early initializer problems. 202 | } 203 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/PlaySlick.md: -------------------------------------------------------------------------------- 1 | # Using Play Slick 2 | 3 | The Play Slick module makes [Slick](https://scala-slick.org/) a first-class citizen of Play, and consists of two primary features: 4 | 5 | - Integration of Slick into Play's application lifecycle. 6 | - Support for [[Play database evolutions|Evolutions]]. 7 | 8 | Play Slick currently supports Slick 3.3 with Play 2.8, for Scala 2.12 and Scala 2.13. 9 | 10 | Previous versions support previous versions of Play! as well as Scala 2.11 and Scala 2.12. 11 | 12 | > **Note**: This guide assumes you already know both Play 2.8 and Slick 3.3. 13 | 14 | ## Getting Help 15 | 16 | If you are having trouble using Play Slick, check if the [[FAQ|PlaySlickFAQ]] contains the answer. Otherwise, feel free to reach out to [Play Discussion Forum](https://github.com/playframework/playframework/discussions). Also, note that if you are seeking help on Slick, the [slick user community](https://scala-slick.org/community/) may be a better place. 17 | 18 | ## About this release 19 | 20 | Users of previous versions of Play Slick will notice a number of major changes. Read the [[migration guide|PlaySlickMigrationGuide]] for details on upgrading from older versions of Play Slick. 21 | 22 | First time users of Play Slick will appreciate the painless integration of Slick into Play. If you're familiar with Play and Slick, configuring and interacting with the Play Slick module will be straightforward. 23 | 24 | ## Setup 25 | 26 | Add a library dependency on play-slick: 27 | 28 | @[add-library-dependencies](code/slick.sbt) 29 | 30 | The above dependency will also bring along the Slick library as a transitive dependency. This implies you don't need to add an explicit dependency on Slick, but you may if desired. You may explicitly define a dependency to Slick if you need to use a newer version than the one bundled with play-slick. Because Slick trailing dot releases are binary compatible, you won't incur any risk in using a different Slick trailing point release than the one that was used to build play-slick. 31 | 32 | ### Support for Play database evolutions 33 | 34 | Play Slick supports [[Play database evolutions|Evolutions]]. 35 | 36 | To enable evolutions, you will need the following dependencies: 37 | 38 | @[add-dependency-with-evolutions](code/slick.sbt) 39 | 40 | Note there is no need to add the Play `evolutions` component to your dependencies, as it is a transitive dependency of the `play-slick-evolutions` module. 41 | 42 | ### JDBC driver dependency 43 | 44 | The Play Slick module does not bundle any JDBC driver. Hence, you will need to explicitly add the JDBC driver(s) you want to use in your application. For instance, if you would like to use an in-memory database such as H2, you will have to add a dependency to it: 45 | 46 | ```scala 47 | "com.h2database" % "h2" % "${H2_VERSION}" // replace `${H2_VERSION}` with an actual version number 48 | ``` 49 | 50 | ## Database Configuration 51 | 52 | To have the Play Slick module handling the lifecycle of Slick databases, it is important that you never create database instances explicitly in your code. Rather, you should provide a valid Slick driver and database configuration in your **application.conf** (by convention the default Slick database must be called `default`): 53 | 54 | ```conf 55 | # Default database configuration 56 | slick.dbs.default.profile="slick.jdbc.H2Profile$" 57 | slick.dbs.default.db.driver="org.h2.Driver" 58 | slick.dbs.default.db.url="jdbc:h2:mem:play" 59 | ``` 60 | 61 | First, note that the above is a valid Slick configuration (for the complete list of configuration parameters that you can use to configure a database see the Slick ScalaDoc for [Database.forConfig](https://scala-slick.org/doc/3.3.2/api/index.html#slick.jdbc.JdbcBackend$DatabaseFactoryDef@forConfig%28path:String,config:com.typesafe.config.Config,driver:java.sql.Driver,classLoader:ClassLoader%29:JdbcBackend.this.Database) - make sure to expand the `forConfig` row in the doc). 62 | 63 | Second, the `slick.dbs` prefix before the database's name is configurable. In fact, you may change it by overriding the value of the configuration key `play.slick.db.config`. 64 | 65 | Third, in the above configuration `slick.dbs.default.profile` is used to configure the Slick profile, while `slick.dbs.default.db.driver` is the underlying JDBC driver used by Slick's backend. In the above configuration we are configuring Slick to use H2 database, but Slick supports several other databases. Check the [Slick documentation](https://scala-slick.org/docs/) for a complete list of supported databases, and to find a matching Slick driver. 66 | 67 | Slick does not support the `DATABASE_URL` environment variable in the same way as the default Play JBDC connection pool. But starting in version 3.0.3, Slick provides a `DatabaseUrlDataSource` specifically for parsing the environment variable. 68 | 69 | ```conf 70 | slick.dbs.default.profile="slick.jdbc.PostgresProfile$" 71 | slick.dbs.default.db.dataSourceClass = "slick.jdbc.DatabaseUrlDataSource" 72 | slick.dbs.default.db.properties.driver = "org.postgresql.Driver" 73 | ``` 74 | 75 | On some platforms, such as Heroku, you may [substitute](https://github.com/lightbend/config/blob/main/HOCON.md#substitution-fallback-to-environment-variables) the `JDBC_DATABASE_URL` environment variable, which is in the format `jdbc:vendor://host:port/db?args`, if it is available. For example: 76 | 77 | ```conf 78 | slick.dbs.default.profile="slick.jdbc.PostgresProfile$" 79 | slick.dbs.default.db.driver="org.postgresql.Driver" 80 | slick.dbs.default.db.url=${JDBC_DATABASE_URL} 81 | ``` 82 | 83 | > **Note**: Failing to provide a valid value for both `slick.dbs.default.profile` and `slick.dbs.default.db.driver` will lead to an exception when trying to run your Play application. 84 | 85 | To configure several databases: 86 | 87 | ```conf 88 | # Orders database 89 | slick.dbs.orders.profile="slick.jdbc.H2Profile$" 90 | slick.dbs.orders.db.driver="org.h2.Driver" 91 | slick.dbs.orders.db.url="jdbc:h2:mem:play" 92 | 93 | # Customers database 94 | slick.dbs.customers.profile="slick.jdbc.H2Profile$" 95 | slick.dbs.customers.db.driver="org.h2.Driver" 96 | slick.dbs.customers.db.url="jdbc:h2:mem:play" 97 | ``` 98 | 99 | If something isn't properly configured, you will be notified in your browser: 100 | 101 | [[images/database-config-error.png]] 102 | 103 | > Note: Your application will be started only if you provide a valid Slick configuration. 104 | 105 | ## Usage 106 | 107 | After having properly configured a Slick database, you can obtain a `DatabaseConfig` (which is a Slick type bundling a database and driver) by using dependency injection. 108 | 109 | > Note: A Slick database instance manages a thread pool and a connection pool. In general, you should not need to shut down a database explicitly in your code (by calling its `close` method), as the Play Slick module takes care of this already. 110 | 111 | ### DatabaseConfig via runtime dependency injection 112 | 113 | While you can get the `DatabaseConfig` instance manually by accessing the `SlickApi`, we've provided some helpers for runtime DI users (Guice, Scaldi, Spring, etc.) for obtaining specific instances within your controller. 114 | 115 | Here is an example of how to inject a `DatabaseConfig` instance for the default database (i.e., the database named `default` in your configuration): 116 | 117 | @[di-database-config](code/DI.scala) 118 | 119 | In this example we're also injecting Play's default `ExecutionContext`, which will be used implicitly in the future transformations below. 120 | 121 | Injecting a `DatabaseConfig` instance for a different database is also easy. You can simply prepend the annotation `@NamedDatabase("")` to the `dbConfigProvider` constructor parameter: 122 | 123 | @[named-di-database-config](code/DI.scala) 124 | 125 | Of course, you should replace the string `""` with the name of the database's configuration you want to use. 126 | 127 | > Note: To access the database object, you need only call the function `db` on the `HasDatabaseConfig` trait. You do not need to reference the dbConfigProvider constructor parameter. 128 | 129 | For a full example, have a look at [this sample project](https://github.com/playframework/play-samples/tree/HEAD/play-scala-slick-example/samples/basic). 130 | 131 | ### Compile-time dependency injection 132 | 133 | If you're using compile-time DI, you can query the database config directly from the `SlickApi` using the `slickApi.dbConfig(DbName(name))` method. The `play.api.db.slick.SlickComponents` provide access to the `slickApi`. 134 | 135 | ### Running a database query in a Controller 136 | 137 | To run a database query in your controller, you will need both a Slick database and driver. Fortunately, from the above we now know how to obtain a Slick `DatabaseConfig`, hence we have what we need to run a database query. 138 | 139 | You will need to import some types and implicits from the driver: 140 | 141 | @[driver-import](code/Example.scala) 142 | 143 | And then you can define a controller's method that will run a database query: 144 | 145 | @[action-with-db](code/Example.scala) 146 | 147 | That's just like using stock Play and Slick! 148 | 149 | ## Configuring the connection pool 150 | 151 | Read [[here|PlaySlickAdvancedTopics#Connection-Pool]] to find out how to configure the connection pool. 152 | -------------------------------------------------------------------------------- /docs/manual/working/scalaGuide/main/sql/slick/PlaySlickMigrationGuide.md: -------------------------------------------------------------------------------- 1 | # Play Slick Migration Guide 2 | 3 | This is a guide for migrating from Play Slick to a version that supports the new Slick 3.0 APIs. 4 | 5 | It assumes you have already migrated your project to use Play 2.5 (see [Play 2.5 Migration Guide](https://www.playframework.com/documentation/2.5.x/Migration25)), that you have read the [Slick documentation](https://scala-slick.org/docs/), and are ready to migrate your Play application to use the new Slick Database I/O Actions API. 6 | 7 | ## Build changes 8 | 9 | Update the Play Slick dependency in your sbt build to match the version provided in the [[Setup|PlaySlick#Setup]] section. 10 | 11 | ### Removed H2 database dependency 12 | 13 | Previous releases of Play Slick used to bundle the H2 database library. That's no longer the case. Hence, if you want to use H2 you will need to explicitly add it to your project's dependencies: 14 | 15 | ```scala 16 | "com.h2database" % "h2" % "${H2_VERSION}" // replace `${H2_VERSION}` with an actual version number 17 | ``` 18 | 19 | ### Evolutions support in a separate module 20 | 21 | Support for [[database evolutions|Evolutions]] used to be included with Play Slick. That's no longer the case. Therefore, if you are using evolutions, you now need to add an additional dependency to `play-slick-evolutions` as explained [[here|PlaySlick#Support-for-Play-database-evolutions]]. 22 | 23 | While, if you are not using evolutions, you can now safely remove `evolutionplugin=disabled` from your `application.conf`. 24 | 25 | ## Database configuration 26 | 27 | With the past releases of Play Slick (which used Slick 2.1 or earlier), you used to configure Slick datasources exactly like you would configure Play JDBC datasources. This is no longer the case, and the following configuration will now be **ignored** by Play Slick: 28 | 29 | ```conf 30 | db.default.driver=org.h2.Driver 31 | db.default.url="jdbc:h2:mem:play" 32 | db.default.user=sa 33 | db.default.password="" 34 | ``` 35 | 36 | There are several reasons for this change. First, the above is not a valid Slick configuration. Second, starting in Slick 3, you configure not just the datasource, but also both a connection pool and a thread pool. Therefore, it makes sense for Play Slick to use an entirely different path for configuring Slick databases. The default path for Slick configuration is now `slick.dbs`. 37 | 38 | Here is how you would need to migrate the above configuration: 39 | 40 | ```conf 41 | slick.dbs.default.profile="slick.jdbc.H2Profile$" # You must provide the required Slick profile! 42 | slick.dbs.default.db.driver=org.h2.Driver 43 | slick.dbs.default.db.url="jdbc:h2:mem:play" 44 | slick.dbs.default.db.user=sa 45 | slick.dbs.default.db.password="" 46 | ``` 47 | 48 | > **Note**: If your database configuration contains settings for the connection pool, be aware that you will need to migrate those settings as well. However, this may be a bit trickier, because Play 2.3 default connection pool used to be BoneCP, while the default Slick 3 connection pool is HikariCP. Read [[here|PlaySlickAdvancedTopics#Connection-Pool]] for how to configure the connection pool. 49 | 50 | ## Automatic Slick driver detection 51 | 52 | Play Slick used to automatically infer the needed Slick driver from the datasource configuration. This feature was removed, hence you must provide the Slick driver to use, for each Slick database configuration, in your **`application.conf`**. 53 | 54 | The rationale for removing this admittedly handy feature is that we want to accept only valid Slick configurations. Furthermore, it's not always possible to automatically detect the correct Slick driver from the database configuration (if this was possible, then Slick would already provide such functionality). 55 | 56 | Therefore, you will need to make the following changes: 57 | 58 | * Each of your Slick database configuration must provide the Slick driver (see [here](#Database-configuration) for an example of how to migrate your database configuration). 59 | * Remove all imports to `import play.api.db.slick.Config.driver.simple._`. 60 | * Read [[here|PlaySlick#Usage]] for how to lookup the Slick driver and database instances (which are needed to use the new Slick 3 Database I/O Actions API). 61 | 62 | ## `DBAction` and `DBSessionRequest` were removed 63 | 64 | Play Slick used to provide a `DBAction` that was useful for: 65 | 66 | * Conveniently pass the Slick `Session` into your Action method. 67 | * Execute the action's body, and hence any blocking call to the database, in a separate thread pool. 68 | * Limiting the number of blocking requests queued in the thread pool (useful to limit application's latency) 69 | 70 | `DBAction` was indeed handy when using Slick 2.1. However, with the new Slick 3 release, we don't need it anymore. The reason is that Slick 3 comes with a new asynchronous API (a.k.a., Database I/O Actions API) that doesn't need the user to manipulate neither a `Session` nor a `Connection`. This makes `DBSessionRequest` and `DBAction`, together with its close friends `CurrentDBAction` and `PredicatedDBAction`, completely obsolete, which is why they have been removed. 71 | 72 | Having said that, migrating your code should be as simple as changing all occurrences of `DBAction` and friends, with the standard Play `Action.async`. Click [[here|PlaySlick#Running-a-database-query-in-a-Controller]] for an example. 73 | 74 | ## Thread Pool 75 | 76 | Play Slick used to provide a separate thread pool for executing controller's actions requiring to access a database. Slick 3 already does exactly this, hence there is no longer a need for Play Slick to create and manage additional thread pools. It follows that the below configuration parameter are effectively obsolete and should be removed from your **applications.conf**: 77 | 78 | ```conf 79 | db.$dbName.maxQueriesPerRequest 80 | slick.db.execution.context 81 | ``` 82 | 83 | The parameter `db.$dbName.maxQueriesPerRequest` was used to limit the number of tasks queued in the thread pool. In Slick 3 you can reach similar results by tuning the configuration parameters `numThreads` and `queueSize`. Read the Slick ScalaDoc for [Database.forConfig](https://scala-slick.org/doc/3.1.0/api/index.html#slick.jdbc.JdbcBackend$DatabaseFactoryDef@forConfig%28path:String,config:com.typesafe.config.Config,driver:java.sql.Driver,classLoader:ClassLoader%29:JdbcBackend.this.Database) (make sure to expand the `forConfig` row in the doc). 84 | 85 | While the parameter `slick.db.execution.context` was used to name the thread pools created by Play Slick. In Slick 3, each thread pool is named using the Slick database configuration path, i.e., if in your **`application.conf`** you have provided a Slick configuration for the database named `default`, then Slick will create a thread pool named `default` for executing the database action on the default database. Note that the name used for the thread pool is not configurable. 86 | 87 | ## `Profile` was removed 88 | 89 | The trait `Profile` was removed and you can use instead `HasDatabaseConfigProvider` or `HasDatabaseConfig` with similar results. 90 | 91 | The trait to use depend on what approach you select to retrieve a Slick database and driver (i.e., an instance of `DatabaseConfig`). If you decide to use dependency injection, then `HasDatabaseConfigProvider` will serve you well. Otherwise, use `HasDatabaseConfig`. 92 | 93 | Read [[here|PlaySlick#Usage]] for a discussion of how to use dependency injection vs global lookup to retrieve an instance of `DatabaseConfig`. 94 | 95 | ## `Database` was removed 96 | 97 | The object `Database` was removed. To retrieve a Slick database and driver (i.e., an instance of `DatabaseConfig`) read [[here|PlaySlick#Usage]]. 98 | 99 | ## `Config` was removed 100 | 101 | The `Config` object, together with `SlickConfig` and `DefaultSlickConfig`, were removed. These abstractions are simply not needed. If you used to call `Config.driver` or `Config.datasource` to retrieve the Slick driver and database, you should now use `DatabaseConfigProvider`. Read [[here|PlaySlick#Usage]] for details. 102 | 103 | ## `SlickPlayIteratees` was removed 104 | 105 | If you were using `SlickPlayIteratees.enumerateSlickQuery` to stream data from the database, you will be happy to know that doing so became a lot easier. Slick 3 implements the [reactive-streams](http://www.reactive-streams.org/) [SPI (Service Provider Interface)](http://en.wikipedia.org/wiki/Service_provider_interface), and Play 2.5 provides a utility class to handily convert a reactive stream into a Play enumerator. 106 | 107 | In Slick, you can obtain a reactive stream by calling the method `stream` on a Slick database instance (instead of the eager `run`). To convert the stream into an enumerator simply call `play.api.libs.streams.Streams.publisherToEnumerator`, passing the stream in argument. 108 | 109 | For a full example, have a look at [this sample project](https://github.com/playframework/play-slick/tree/2.0.0/samples/iteratee). 110 | 111 | ## DDL support was removed 112 | 113 | Previous versions of Play Slick included a DDL plugin which would read your Slick tables definitions, and automatically creates schema updates on reload. While this is an interesting and useful feature, the underlying implementation was fragile, and and relied on the assumption that your tables would be accessible via a module (i.e., a Scala `object`). This coding pattern was possible because Play Slick allowed to import the Slick driver available via a top-level import. However, because support for [automatic detection of the Slick driver](#Automatic-Slick-driver-detection) was removed, you will not declare a top-level import for the Slick driver. This implies that Slick tables will no longer be accessible via a module. This fact breaks the assumption made in the initial implementation of the DDL plugin, and it's the reason why the feature was removed. 114 | 115 | The consequence of the above is that you are in charge of creating and managing your project's database schema. Therefore, whenever you make a change a Slick table in the code, make sure to also update the database schema. If you find it tedious to manually keep in sync your database schema and the related table definition in your code, you may want to have a look at the code generation feature available in Slick. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------