├── project ├── build.properties └── plugins.sbt ├── sbt-launch.jar ├── scalax15-slick-slides.key ├── scalax15-slick-slides.pdf ├── sbt.bat ├── sbt.sh ├── src └── main │ ├── resources │ ├── application.conf │ └── logback.xml │ └── scala │ ├── joins │ ├── Rating.scala │ └── Main.scala │ ├── tables │ ├── Rating.scala │ └── Main.scala │ ├── actions │ ├── Rating.scala │ └── Main.scala │ ├── queries │ ├── Rating.scala │ └── Main.scala │ ├── helloworld │ └── Main.scala │ └── profiles │ └── Main.scala ├── scalax15-slick.sublime-project ├── .gitignore └── README.md /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.16 2 | -------------------------------------------------------------------------------- /sbt-launch.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underscoreio/scalax15-slick/HEAD/sbt-launch.jar -------------------------------------------------------------------------------- /scalax15-slick-slides.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underscoreio/scalax15-slick/HEAD/scalax15-slick-slides.key -------------------------------------------------------------------------------- /scalax15-slick-slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/underscoreio/scalax15-slick/HEAD/scalax15-slick-slides.pdf -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0") 2 | 3 | addSbtPlugin("org.ensime" % "ensime-sbt" % "0.2.1") 4 | 5 | -------------------------------------------------------------------------------- /sbt.bat: -------------------------------------------------------------------------------- 1 | java -Xmx3g -Xms3g -XX:+TieredCompilation -XX:ReservedCodeCacheSize=256m -XX:+UseNUMA -XX:+UseParallelGC -XX:+CMSClassUnloadingEnabled -jar sbt-launch.jar %* 2 | -------------------------------------------------------------------------------- /sbt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | java -Xmx3g -Xms3g -XX:+TieredCompilation -XX:ReservedCodeCacheSize=256m -XX:+UseNUMA -XX:+UseParallelGC -XX:+CMSClassUnloadingEnabled -jar sbt-launch.jar "$@" 4 | -------------------------------------------------------------------------------- /src/main/resources/application.conf: -------------------------------------------------------------------------------- 1 | scalaxdb = { 2 | connectionPool = disabled 3 | url = "jdbc:h2:mem:scalaxdb" 4 | driver = "org.h2.Driver" 5 | keepAliveConnection = true 6 | } 7 | -------------------------------------------------------------------------------- /scalax15-slick.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "follow_symlinks": true, 6 | "path": ".", 7 | "folder_exclude_patterns": [ 8 | ".git", 9 | "project/project", 10 | "target", 11 | "*.oo3" 12 | ], 13 | "file_exclude_patterns": [ 14 | ".gitignore", 15 | "README.md", 16 | "sbt-launch.jar", 17 | "sbt.bat", 18 | "sbt.sh", 19 | "*.key", 20 | "*.sublime-project" 21 | ] 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %-5level %date{ISO8601} %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/scala/joins/Rating.scala: -------------------------------------------------------------------------------- 1 | package joins 2 | 3 | import slick.driver.H2Driver.api._ 4 | 5 | sealed abstract class Rating(val stars: Int) 6 | 7 | object Rating { 8 | final case object Awesome extends Rating(5) 9 | final case object Good extends Rating(4) 10 | final case object NotBad extends Rating(3) 11 | final case object Meh extends Rating(2) 12 | final case object Aaargh extends Rating(1) 13 | 14 | implicit val columnType: BaseColumnType[Rating] = 15 | MappedColumnType.base[Rating, Int](Rating.toInt, Rating.fromInt) 16 | 17 | private def fromInt(stars: Int): Rating = stars match { 18 | case 5 => Awesome 19 | case 4 => Good 20 | case 3 => NotBad 21 | case 2 => Meh 22 | case 1 => Aaargh 23 | case _ => sys.error("Ratings only apply from 1 to 5") 24 | } 25 | 26 | private def toInt(rating: Rating): Int = rating match { 27 | case Awesome => 5 28 | case Good => 4 29 | case NotBad => 3 30 | case Meh => 2 31 | case Aaargh => 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/tables/Rating.scala: -------------------------------------------------------------------------------- 1 | package tables 2 | 3 | import slick.driver.H2Driver.api._ 4 | 5 | sealed abstract class Rating(val stars: Int) 6 | 7 | object Rating { 8 | final case object Awesome extends Rating(5) 9 | final case object Good extends Rating(4) 10 | final case object NotBad extends Rating(3) 11 | final case object Meh extends Rating(2) 12 | final case object Aaargh extends Rating(1) 13 | 14 | implicit val columnType: BaseColumnType[Rating] = 15 | MappedColumnType.base[Rating, Int](Rating.toInt, Rating.fromInt) 16 | 17 | private def fromInt(stars: Int): Rating = stars match { 18 | case 5 => Awesome 19 | case 4 => Good 20 | case 3 => NotBad 21 | case 2 => Meh 22 | case 1 => Aaargh 23 | case _ => sys.error("Ratings only apply from 1 to 5") 24 | } 25 | 26 | private def toInt(rating: Rating): Int = rating match { 27 | case Awesome => 5 28 | case Good => 4 29 | case NotBad => 3 30 | case Meh => 2 31 | case Aaargh => 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/actions/Rating.scala: -------------------------------------------------------------------------------- 1 | package actions 2 | 3 | import slick.driver.H2Driver.api._ 4 | 5 | sealed abstract class Rating(val stars: Int) 6 | 7 | object Rating { 8 | final case object Awesome extends Rating(5) 9 | final case object Good extends Rating(4) 10 | final case object NotBad extends Rating(3) 11 | final case object Meh extends Rating(2) 12 | final case object Aaargh extends Rating(1) 13 | 14 | implicit val columnType: BaseColumnType[Rating] = 15 | MappedColumnType.base[Rating, Int](Rating.toInt, Rating.fromInt) 16 | 17 | private def fromInt(stars: Int): Rating = stars match { 18 | case 5 => Awesome 19 | case 4 => Good 20 | case 3 => NotBad 21 | case 2 => Meh 22 | case 1 => Aaargh 23 | case _ => sys.error("Ratings only apply from 1 to 5") 24 | } 25 | 26 | private def toInt(rating: Rating): Int = rating match { 27 | case Awesome => 5 28 | case Good => 4 29 | case NotBad => 3 30 | case Meh => 2 31 | case Aaargh => 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/queries/Rating.scala: -------------------------------------------------------------------------------- 1 | package queries 2 | 3 | import slick.driver.H2Driver.api._ 4 | 5 | sealed abstract class Rating(val stars: Int) 6 | 7 | object Rating { 8 | final case object Awesome extends Rating(5) 9 | final case object Good extends Rating(4) 10 | final case object NotBad extends Rating(3) 11 | final case object Meh extends Rating(2) 12 | final case object Aaargh extends Rating(1) 13 | 14 | implicit val columnType: BaseColumnType[Rating] = 15 | MappedColumnType.base[Rating, Int](Rating.toInt, Rating.fromInt) 16 | 17 | private def fromInt(stars: Int): Rating = stars match { 18 | case 5 => Awesome 19 | case 4 => Good 20 | case 3 => NotBad 21 | case 2 => Meh 22 | case 1 => Aaargh 23 | case _ => sys.error("Ratings only apply from 1 to 5") 24 | } 25 | 26 | private def toInt(rating: Rating): Int = rating match { 27 | case Awesome => 5 28 | case Good => 4 29 | case NotBad => 3 30 | case Meh => 2 31 | case Aaargh => 1 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/scala/helloworld/Main.scala: -------------------------------------------------------------------------------- 1 | package helloworld 2 | 3 | import scala.concurrent._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | 7 | import slick.driver.H2Driver.api._ 8 | 9 | object Main { 10 | 11 | // Tables ------------------------------------- 12 | 13 | case class Album( 14 | artist : String, 15 | title : String, 16 | id : Long = 0L) 17 | 18 | class AlbumTable(tag: Tag) extends Table[Album](tag, "albums") { 19 | def artist = column[String]("artist") 20 | def title = column[String]("title") 21 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 22 | 23 | def * = (artist, title, id) <> (Album.tupled, Album.unapply) 24 | } 25 | 26 | lazy val AlbumTable = TableQuery[AlbumTable] 27 | 28 | 29 | 30 | // Actions ------------------------------------ 31 | 32 | val createTableAction = 33 | AlbumTable.schema.create 34 | 35 | val insertAlbumsAction = 36 | AlbumTable ++= Seq( 37 | Album( "Keyboard Cat" , "Keyboard Cat's Greatest Hits" ), 38 | Album( "Spice Girls" , "Spice" ), 39 | Album( "Rick Astley" , "Whenever You Need Somebody" ), 40 | Album( "Manowar" , "The Triumph of Steel" ), 41 | Album( "Justin Bieber" , "Believe" )) 42 | 43 | val selectAlbumsAction = 44 | AlbumTable.result 45 | 46 | 47 | 48 | // Database ----------------------------------- 49 | 50 | val db = Database.forConfig("scalaxdb") 51 | 52 | 53 | 54 | // Let's go! ---------------------------------- 55 | 56 | def exec[T](action: DBIO[T]): T = 57 | Await.result(db.run(action), 2 seconds) 58 | 59 | def main(args: Array[String]): Unit = { 60 | exec(createTableAction) 61 | exec(insertAlbumsAction) 62 | exec(selectAlbumsAction).foreach(println) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/scala/tables/Main.scala: -------------------------------------------------------------------------------- 1 | package tables 2 | 3 | import scala.concurrent._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | 7 | import slick.driver.H2Driver.api._ 8 | 9 | object Main { 10 | 11 | // Tables ------------------------------------- 12 | 13 | case class Album( 14 | artist : String, 15 | title : String, 16 | id : Long = 0L) 17 | 18 | class AlbumTable(tag: Tag) extends Table[Album](tag, "albums") { 19 | def artist = column[String]("artist") 20 | def title = column[String]("title") 21 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 22 | 23 | def * = (artist, title, id) <> (Album.tupled, Album.unapply) 24 | } 25 | 26 | lazy val AlbumTable = TableQuery[AlbumTable] 27 | 28 | 29 | 30 | // Actions ------------------------------------ 31 | 32 | val createTableAction = 33 | AlbumTable.schema.create 34 | 35 | val insertAlbumsAction = 36 | AlbumTable ++= Seq( 37 | Album( "Keyboard Cat" , "Keyboard Cat's Greatest Hits" ), // released in 2009 38 | Album( "Spice Girls" , "Spice" ), // released in 1996 39 | Album( "Rick Astley" , "Whenever You Need Somebody" ), // released in 1987 40 | Album( "Manowar" , "The Triumph of Steel" ), // released in 1992 41 | Album( "Justin Bieber" , "Believe" )) // released in 2013 42 | 43 | val selectAlbumsAction = 44 | AlbumTable.result 45 | 46 | 47 | 48 | // Database ----------------------------------- 49 | 50 | val db = Database.forConfig("scalaxdb") 51 | 52 | 53 | 54 | // Let's go! ---------------------------------- 55 | 56 | def exec[T](action: DBIO[T]): T = 57 | Await.result(db.run(action), 2 seconds) 58 | 59 | def main(args: Array[String]): Unit = { 60 | exec(createTableAction) 61 | exec(insertAlbumsAction) 62 | exec(selectAlbumsAction).foreach(println) 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/scala,intellij,eclipse,sbt 2 | 3 | ### Scala ### 4 | *.class 5 | *.log 6 | 7 | # sbt specific 8 | .cache 9 | .cache-main 10 | .history 11 | .lib/ 12 | dist/* 13 | target/ 14 | lib_managed/ 15 | src_managed/ 16 | project/boot/ 17 | project/plugins/project/ 18 | 19 | # Scala-IDE specific 20 | .scala_dependencies 21 | .worksheet 22 | 23 | 24 | ### SublimeText ### 25 | # cache files for sublime text 26 | *.tmlanguage.cache 27 | *.tmPreferences.cache 28 | *.stTheme.cache 29 | 30 | # workspace files are user-specific 31 | *.sublime-workspace 32 | 33 | # project files should be checked into the repository, unless a significant 34 | # proportion of contributors will probably not be using SublimeText 35 | # *.sublime-project 36 | 37 | # sftp configuration file 38 | sftp-config.json 39 | 40 | 41 | ### Intellij ### 42 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 43 | 44 | *.iml 45 | 46 | ## Directory-based project format: 47 | .idea/ 48 | # if you remove the above rule, at least ignore the following: 49 | 50 | # User-specific stuff: 51 | # .idea/workspace.xml 52 | # .idea/tasks.xml 53 | # .idea/dictionaries 54 | 55 | # Sensitive or high-churn files: 56 | # .idea/dataSources.ids 57 | # .idea/dataSources.xml 58 | # .idea/sqlDataSources.xml 59 | # .idea/dynamic.xml 60 | # .idea/uiDesigner.xml 61 | 62 | # Gradle: 63 | # .idea/gradle.xml 64 | # .idea/libraries 65 | 66 | # Mongo Explorer plugin: 67 | # .idea/mongoSettings.xml 68 | 69 | ## File-based project format: 70 | *.ipr 71 | *.iws 72 | 73 | ## Plugin-specific files: 74 | 75 | # IntelliJ 76 | /out/ 77 | 78 | # mpeltonen/sbt-idea plugin 79 | .idea_modules/ 80 | 81 | # JIRA plugin 82 | atlassian-ide-plugin.xml 83 | 84 | # Crashlytics plugin (for Android Studio and IntelliJ) 85 | com_crashlytics_export_strings.xml 86 | crashlytics.properties 87 | crashlytics-build.properties 88 | 89 | 90 | ### Eclipse ### 91 | *.pydevproject 92 | .metadata 93 | .gradle 94 | bin/ 95 | tmp/ 96 | *.tmp 97 | *.bak 98 | *.swp 99 | *~.nib 100 | local.properties 101 | .settings/ 102 | .loadpath 103 | 104 | # Eclipse Core 105 | .project 106 | 107 | # External tool builders 108 | .externalToolBuilders/ 109 | 110 | # Locally stored "Eclipse launch configurations" 111 | *.launch 112 | 113 | # CDT-specific 114 | .cproject 115 | 116 | # JDT-specific (Eclipse Java Development Tools) 117 | .classpath 118 | 119 | # Java annotation processor (APT) 120 | .factorypath 121 | 122 | # PDT-specific 123 | .buildpath 124 | 125 | # sbteclipse plugin 126 | .target 127 | 128 | # TeXlipse plugin 129 | .texlipse 130 | 131 | 132 | # Ensime 133 | 134 | .ensime 135 | .ensime_cache 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Slick Workshop for Scala Exchange 2015 2 | ====================================== 3 | 4 | Slides and companion material for the Slick workshop from [Scala Exchange 2015]. 5 | 6 | Copyright 2015 [Dave Gurnell] of [Underscore]. 7 | Slides and course content licensed [CC-BY-NC-SA 4.0], 8 | code samples licensed [Apache 2.0]. 9 | 10 | [![Join the chat at https://gitter.im/underscoreio/scalax15-slick](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/underscoreio/scalax15-slick?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 11 | 12 | # Quick Start 13 | 14 | A [screencast] of the workshop content is available on Vimeo. 15 | 16 | Follow the instructions below to get set up. 17 | Doing so will prevent you having to download the internet on conference wifi! 18 | You will need a Java 8 compatible JVM and a familiar programmer's text editor or IDE. 19 | If you have any problems please let me know on the [Gitter channel]. 20 | 21 | 1. Clone this repo and switch to the root directory: 22 | 23 | ~~~ bash 24 | $ git clone https://github.com/underscoreio/scalax15-slick.git 25 | 26 | $ cd scalax15-slick 27 | ~~~ 28 | 29 | 2. Run SBT: 30 | 31 | ~~~ bash 32 | $ ./sbt.sh # ".\sbt.bat" on Windows 33 | ~~~ 34 | 35 | 3. Compile and run the example "helloworld.Main" application. 36 | This will take a few minutes to run the first time. 37 | You'll need an internet connection to download dependencies: 38 | 39 | ~~~ bash 40 | sbt> runMain helloworld.Main 41 | ~~~ 42 | 43 | 4. If you see a list of albums similar to the following, you're good: 44 | 45 | ~~~ 46 | Album(Keyboard Cat,Keyboard Cat's Greatest Hits,1) 47 | Album(Spice Girls,Spice,2) 48 | Album(Rick Astley,Whenever You Need Somebody,3) 49 | Album(Manowar,The Triumph of Steel,4) 50 | Album(Justin Bieber,Believe,5) 51 | ~~~ 52 | 53 | If not, let me know on the [Gitter channel]. 54 | 55 | 5. If you use an IDE that requires further setup, do that now. 56 | I've included the `sbteclipse` and `ensime-sbt` plugins in the build. 57 | 58 | Looking forward to seeing you at the workshop! 59 | 60 | # Further Reading 61 | 62 | The content for the workshop will be based heavily on the book 63 | [Essential Slick] by Richard Dallaway and Jonathan Ferguson. 64 | 65 | Sign up to the [Underscore newsletter] to receive news, views, 66 | and code samples about Scala, Slick, and functional programming. 67 | 68 | [screencast]: https://vimeo.com/148074461 69 | [Essential Slick]: http://underscore.io/books/essential-slick 70 | [Scala Exchange 2015]: http://scala.exchange 71 | [Dave Gurnell]: http://davegurnell.com 72 | [Underscore]: http://underscore.io 73 | [CC-BY-NC-SA 4.0]: http://creativecommons.org/licenses/by-nc-sa/4.0/ 74 | [Apache 2]: http://www.apache.org/licenses/LICENSE-2.0 75 | [Underscore newsletter]: http://underscore.io/newsletter.html 76 | [Gitter channel]: https://gitter.im/underscoreio/scalax15-slick -------------------------------------------------------------------------------- /src/main/scala/actions/Main.scala: -------------------------------------------------------------------------------- 1 | package actions 2 | 3 | import scala.concurrent._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | 7 | import slick.dbio.DBIOAction 8 | import slick.profile.SqlAction 9 | 10 | import slick.driver.H2Driver.api._ 11 | 12 | object Main { 13 | 14 | // Tables ------------------------------------- 15 | 16 | case class Album( 17 | artist : String, 18 | title : String, 19 | year : Int, 20 | rating : Rating, 21 | id : Long = 0L) 22 | 23 | class AlbumTable(tag: Tag) extends Table[Album](tag, "albums") { 24 | def artist = column[String]("artist") 25 | def title = column[String]("title") 26 | def year = column[Int]("year") 27 | def rating = column[Rating]("rating") 28 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 29 | 30 | def * = (artist, title, year, rating, id) <> (Album.tupled, Album.unapply) 31 | } 32 | 33 | lazy val AlbumTable = TableQuery[AlbumTable] 34 | 35 | 36 | 37 | // Schema actions ----------------------------- 38 | 39 | val createTableAction = 40 | AlbumTable.schema.create 41 | 42 | val dropTableAction = 43 | AlbumTable.schema.drop 44 | 45 | 46 | 47 | // Select actions ----------------------------- 48 | 49 | val selectAction = 50 | AlbumTable 51 | .filter(_.artist === "Keyboard Cat") 52 | .map(_.title) 53 | .result 54 | 55 | 56 | 57 | // Update actions ----------------------------- 58 | 59 | val updateAction = 60 | AlbumTable 61 | .filter(_.artist === "Keyboard Cat") 62 | .map(_.title) 63 | .update("Even Greater Hits") 64 | 65 | val updateAction2 = 66 | AlbumTable 67 | .filter(_.artist === "Keyboard Cat") 68 | .map(a => (a.title, a.year)) 69 | .update(("Even Greater Hits", 2010)) 70 | 71 | 72 | 73 | // Delete actions ----------------------------- 74 | 75 | val deleteAction = 76 | AlbumTable 77 | .filter(_.artist === "Justin Bieber") 78 | .delete 79 | 80 | 81 | 82 | // Insert actions ----------------------------- 83 | 84 | val insertOneAction = 85 | AlbumTable += Album("Pink Floyd", "Dark Side of the Moon", 1978, Rating.Awesome ) 86 | 87 | val insertAllAction = 88 | AlbumTable ++= Seq( 89 | Album( "Keyboard Cat" , "Keyboard Cat's Greatest Hits" , 2009 , Rating.Awesome ), 90 | Album( "Spice Girls" , "Spice" , 1996 , Rating.Good ), 91 | Album( "Rick Astley" , "Whenever You Need Somebody" , 1987 , Rating.NotBad ), 92 | Album( "Manowar" , "The Triumph of Steel" , 1992 , Rating.Meh ), 93 | Album( "Justin Bieber" , "Believe" , 2013 , Rating.Aaargh )) 94 | 95 | 96 | 97 | // Database ----------------------------------- 98 | 99 | val db = Database.forConfig("scalaxdb") 100 | 101 | 102 | 103 | // Let's go! ---------------------------------- 104 | 105 | def exec[T](action: DBIO[T]): T = 106 | Await.result(db.run(action), 2 seconds) 107 | 108 | def main(args: Array[String]): Unit = { 109 | exec(createTableAction) 110 | exec(insertAllAction) 111 | exec(insertOneAction) 112 | exec(selectAction).foreach(println) 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/scala/queries/Main.scala: -------------------------------------------------------------------------------- 1 | package queries 2 | 3 | import scala.concurrent._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | 7 | import slick.driver.H2Driver.api._ 8 | 9 | object Main { 10 | 11 | // Tables ------------------------------------- 12 | 13 | case class Album( 14 | artist : String, 15 | title : String, 16 | year : Int, 17 | rating : Rating, 18 | id : Long = 0L) 19 | 20 | class AlbumTable(tag: Tag) extends Table[Album](tag, "albums") { 21 | def artist = column[String]("artist") 22 | def title = column[String]("title") 23 | def year = column[Int]("year") 24 | def rating = column[Rating]("rating") 25 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 26 | 27 | def * = (artist, title, year, rating, id) <> (Album.tupled, Album.unapply) 28 | } 29 | 30 | lazy val AlbumTable = TableQuery[AlbumTable] 31 | 32 | 33 | 34 | // Example queries ---------------------------- 35 | 36 | val createTableAction = 37 | AlbumTable.schema.create 38 | 39 | val insertAlbumsAction = 40 | AlbumTable ++= Seq( 41 | Album( "Keyboard Cat" , "Keyboard Cat's Greatest Hits" , 2009 , Rating.Awesome ), 42 | Album( "Spice Girls" , "Spice" , 1996 , Rating.Good ), 43 | Album( "Rick Astley" , "Whenever You Need Somebody" , 1987 , Rating.NotBad ), 44 | Album( "Manowar" , "The Triumph of Steel" , 1992 , Rating.Meh ), 45 | Album( "Justin Bieber" , "Believe" , 2013 , Rating.Aaargh )) 46 | 47 | val selectAllQuery = 48 | AlbumTable 49 | 50 | val selectWhereQuery = 51 | AlbumTable 52 | .filter(_.rating === (Rating.Awesome : Rating)) 53 | 54 | val selectSortedQuery1 = 55 | AlbumTable 56 | .sortBy(_.year.asc) 57 | 58 | val selectSortedQuery2 = 59 | AlbumTable 60 | .sortBy(a => (a.year.asc, a.rating.asc)) 61 | 62 | val selectPagedQuery = 63 | AlbumTable 64 | .drop(2).take(1) 65 | 66 | val selectColumnsQuery1 = 67 | AlbumTable 68 | .map(_.title) 69 | 70 | val selectColumnsQuery2 = 71 | AlbumTable 72 | .map(a => (a.artist, a.title)) 73 | 74 | val selectCombinedQuery = 75 | AlbumTable 76 | .filter(_.artist === "Keyboard Cat") 77 | .map(_.title) 78 | 79 | 80 | 81 | // Returning single/multiple results ---------- 82 | 83 | val selectPagedAction1 = 84 | selectPagedQuery 85 | .result 86 | 87 | val selectPagedAction2 = 88 | selectPagedQuery 89 | .result 90 | .headOption 91 | 92 | val selectPagedAction3 = 93 | selectPagedQuery 94 | .result 95 | .head 96 | 97 | 98 | 99 | // Database ----------------------------------- 100 | 101 | val db = Database.forConfig("scalaxdb") 102 | 103 | 104 | 105 | // Let's go! ---------------------------------- 106 | 107 | def exec[T](action: DBIO[T]): T = 108 | Await.result(db.run(action), 2 seconds) 109 | 110 | def createTestAlbums() = { 111 | exec(createTableAction) 112 | exec(insertAlbumsAction) 113 | } 114 | 115 | def main(args: Array[String]): Unit = { 116 | createTestAlbums() 117 | exec(selectCombinedQuery.result).foreach(println) 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/scala/profiles/Main.scala: -------------------------------------------------------------------------------- 1 | package profiles 2 | 3 | import scala.concurrent._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | 7 | import slick.driver.{JdbcProfile, H2Driver} 8 | 9 | trait DatabaseProfile { 10 | val profile: JdbcProfile 11 | 12 | import profile.api._ 13 | } 14 | 15 | 16 | 17 | trait ArtistDatabaseModule { 18 | self: DatabaseProfile => 19 | 20 | import profile.api._ 21 | 22 | case class Artist( 23 | name : String, 24 | id : Long = 0L) 25 | 26 | class ArtistTable(tag: Tag) extends Table[Artist](tag, "artists") { 27 | def name = column[String]("name") 28 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 29 | def * = (name, id) <> (Artist.tupled, Artist.unapply) 30 | } 31 | 32 | lazy val ArtistTable = TableQuery[ArtistTable] 33 | } 34 | 35 | 36 | 37 | trait AlbumDatabaseModule { 38 | self: DatabaseProfile with ArtistDatabaseModule => 39 | 40 | import profile.api._ 41 | 42 | case class Album( 43 | artistId : Long, 44 | title : String, 45 | id : Long = 0L) 46 | 47 | class AlbumTable(tag: Tag) extends Table[Album](tag, "albums") { 48 | def artistId = column[Long]("artistId") 49 | def title = column[String]("title") 50 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 51 | def * = (artistId, title, id) <> (Album.tupled, Album.unapply) 52 | } 53 | 54 | lazy val AlbumTable = TableQuery[AlbumTable] 55 | 56 | val selectAllAction: DBIO[Seq[(Artist, Album)]] = 57 | ArtistTable.join(AlbumTable) 58 | .on { case (artist, album) => artist.id === album.artistId } 59 | .sortBy { case (artist, album) => artist.name.asc } 60 | .result 61 | } 62 | 63 | 64 | 65 | trait TestDataModule { 66 | self: DatabaseProfile with ArtistDatabaseModule with AlbumDatabaseModule => 67 | 68 | import profile.api._ 69 | 70 | val createTablesAction = 71 | ArtistTable.schema.create andThen 72 | AlbumTable.schema.create 73 | 74 | val dropTablesAction = 75 | AlbumTable.schema.drop andThen 76 | ArtistTable.schema.drop 77 | 78 | val insertAllAction: DBIOAction[Unit, NoStream, Effect.Write] = 79 | for { 80 | keyboardCatId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "Keyboard Cat" ) 81 | spiceGirlsId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "Spice Girls" ) 82 | rickAstleyId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "Rick Astley" ) 83 | _ <- AlbumTable ++= Seq( 84 | Album( keyboardCatId , "Keyboard Cat's Greatest Hits" ), 85 | Album( spiceGirlsId , "Spice" ), 86 | Album( rickAstleyId , "Whenever You Need Somebody" )) 87 | } yield () 88 | 89 | val doEverythingAction: DBIO[Seq[(Artist, Album)]] = ( 90 | createTablesAction andThen 91 | insertAllAction andThen 92 | selectAllAction 93 | ).transactionally 94 | } 95 | 96 | 97 | 98 | class DatabaseLayer[A <: JdbcProfile](val profile: JdbcProfile) extends DatabaseProfile 99 | with ArtistDatabaseModule 100 | with AlbumDatabaseModule 101 | with TestDataModule { 102 | 103 | import profile.api._ 104 | 105 | val db = Database.forConfig("scalaxdb") 106 | 107 | def exec[T](action: DBIO[T]): T = 108 | Await.result(db.run(action), 2 seconds) 109 | } 110 | 111 | 112 | object Main { 113 | val databaselayer = new DatabaseLayer(H2Driver) 114 | 115 | import databaselayer._ 116 | 117 | def main(args: Array[String]): Unit = { 118 | 119 | exec(doEverythingAction).foreach(println) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/scala/joins/Main.scala: -------------------------------------------------------------------------------- 1 | package joins 2 | 3 | import scala.concurrent._ 4 | import scala.concurrent.duration._ 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | 7 | import slick.dbio.DBIOAction 8 | import slick.profile.SqlAction 9 | 10 | import slick.driver.H2Driver.api._ 11 | 12 | object Main { 13 | 14 | // Tables ------------------------------------- 15 | 16 | case class Artist( 17 | name : String, 18 | id : Long = 0L) 19 | 20 | class ArtistTable(tag: Tag) extends Table[Artist](tag, "artists") { 21 | def name = column[String]("name") 22 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 23 | 24 | def * = (name, id) <> (Artist.tupled, Artist.unapply) 25 | } 26 | 27 | lazy val ArtistTable = TableQuery[ArtistTable] 28 | 29 | case class Album( 30 | artistId : Long, 31 | title : String, 32 | year : Int, 33 | rating : Rating, 34 | id : Long = 0L) 35 | 36 | class AlbumTable(tag: Tag) extends Table[Album](tag, "albums") { 37 | def artistId = column[Long]("artistId") 38 | def title = column[String]("title") 39 | def year = column[Int]("year") 40 | def rating = column[Rating]("rating") 41 | def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 42 | 43 | def * = (artistId, title, year, rating, id) <> (Album.tupled, Album.unapply) 44 | } 45 | 46 | lazy val AlbumTable = TableQuery[AlbumTable] 47 | 48 | 49 | 50 | // Setup -------------------------------------- 51 | 52 | val createTablesAction = 53 | ArtistTable.schema.create andThen 54 | AlbumTable.schema.create 55 | 56 | val dropTablesAction = 57 | AlbumTable.schema.drop andThen 58 | ArtistTable.schema.drop 59 | 60 | val insertAllAction: DBIOAction[Unit, NoStream, Effect.Write] = 61 | for { 62 | keyboardCatId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "Keyboard Cat" ) 63 | spiceGirlsId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "Spice Girls" ) 64 | rickAstleyId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "Rick Astley" ) 65 | myMatesBandId <- ArtistTable returning ArtistTable.map(_.id) += Artist( "My Mate's Band" ) 66 | _ <- AlbumTable ++= Seq( 67 | Album( keyboardCatId , "Keyboard Cat's Greatest Hits" , 2009 , Rating.Awesome ), 68 | Album( spiceGirlsId , "Spice" , 1996 , Rating.Good ), 69 | Album( spiceGirlsId , "Forever" , 2000 , Rating.Meh ), 70 | Album( rickAstleyId , "Whenever You Need Somebody" , 1987 , Rating.Awesome ), 71 | Album( rickAstleyId , "Hold Me in Your Arms" , 1988 , Rating.Good ), 72 | Album( rickAstleyId , "Free" , 1991 , Rating.Meh ), 73 | Album( rickAstleyId , "Body & Soul" , 1993 , Rating.Meh ), 74 | Album( rickAstleyId , "Keep It Turned On" , 2001 , Rating.Meh ), 75 | Album( rickAstleyId , "Portrait" , 2005 , Rating.NotBad ), 76 | Album( rickAstleyId , "My Red Book" , 2013 , Rating.Meh )) 77 | } yield () 78 | 79 | 80 | 81 | // Implicit joins ----------------------------- 82 | 83 | val implicitInnerJoin: DBIOAction[Seq[(Artist, Album)], NoStream, Effect.Read] = { 84 | val query = for { 85 | artist <- ArtistTable 86 | album <- AlbumTable if artist.id === album.artistId 87 | } yield (artist, album) 88 | 89 | query.result 90 | } 91 | 92 | 93 | 94 | // Explicit joins ----------------------------- 95 | 96 | val explicitInnerJoin: DBIOAction[Seq[(Artist, Album)], NoStream, Effect.Read] = 97 | ArtistTable.join(AlbumTable) 98 | .on { case (artist, album) => artist.id === album.artistId } 99 | .result 100 | 101 | 102 | 103 | // Database ----------------------------------- 104 | 105 | val db = Database.forConfig("scalaxdb") 106 | 107 | 108 | 109 | // Let's go! ---------------------------------- 110 | 111 | def exec[T](action: DBIO[T]): T = 112 | Await.result(db.run(action), 2 seconds) 113 | 114 | def resultsToString[T](message: String)(results: Seq[T]): String = 115 | message + results.mkString("\n ", "\n ", "") 116 | 117 | def main(args: Array[String]): Unit = { 118 | val everythingAction = 119 | createTablesAction andThen 120 | insertAllAction andThen 121 | DBIO.sequence(Seq( 122 | implicitInnerJoin map (resultsToString("Implicit inner join: ")), 123 | explicitInnerJoin map (resultsToString("Explicit inner join: ")) 124 | )) 125 | 126 | exec(everythingAction.transactionally).foreach(println) 127 | } 128 | 129 | } 130 | --------------------------------------------------------------------------------