So Slick! An introduction

So Slick! An introduction
Slide Note
Embed
Share

Slick is a declarative and stateless ORM tool for Scala, offering a natural fit with relational databases. Explore its Scala collection-like API, predictable SQL structure, type-safety, schema consistency enforcement, and small configuration using Scala code.

  • Slick
  • Functional Relational Mapper
  • Scala
  • Declarative
  • ORM

Uploaded on Feb 15, 2025 | 1 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.

E N D

Presentation Transcript


  1. So Slick! An introduction Jan Christopher Vogt, EPFL Slick Team Scala User Group Berlin Brandenburg

  2. (vs. ORM) Functional-Relational Mapper natural fit (no impedance mismatch) declarative embraces relational stateless Slick is to ORM what Scala is to Java

  3. 8 Reasons for using Slick

  4. 1 Scala collection-like API

  5. Scala collection-like API Device for ( d <- Devices; if d.price > 1000.0 ) yield d.acquisition id: Long price: Double acquisition: Date Devices .filter(_.price > 1000.0) .map(_.acquisition)

  6. 2 Predictable SQL structure

  7. Predictable SQL structure Devices .filter(_.price > 1000.0) .map(_.acquisition) .selectStatement select x2."ACQUISITION" from "DEVICE" x2 where x2."PRICE" > 1000.0

  8. 3 Type-safety

  9. Compile-Time Safety Spelling mistake in column name? Wrong column type? Query doesn t match expected result type? scalac sees it all!

  10. Caution: Error messages can be bad Error messages can destroy the illusion

  11. Enforce schema consistency Generate DDL from table classes Slick 2.0: Generate table classes and mapped classes from database

  12. 4 Small configuration using Scala code

  13. Table description class class Devices(tag: Tag) Devices(tag: Tag) extends extends Table[ def def id = column[Long] ( id = column[Long] ("ID" def def price = column[Double]( price = column[Double]("PRICE" def def acquisition = column[Date] ( acquisition = column[Date] ("ACQUISITION" def def * = (id, price, acquisition) * = (id, price, acquisition) Table[(Long, Double, Date) (Long, Double, Date)]( ](tag, tag,"DEVICES "DEVICES" ") { "ID", , O O. .PrimaryKey PrimaryKey) ) "PRICE") ) "ACQUISITION") ) ) { } def def Devices = Devices = TableQuery TableQuery[Devices] [Devices] can be auto-generated in Slick 2.0

  14. Connect import scala.slick.driver.H2Driver.simple._ val db = Database.forURL( "jdbc:h2:mem:testdb", "org.h2.Driver") db.withTransaction { implicit session => // <- run queries here }

  15. 5 Explicit control over execution and transfer

  16. Execution control Device val query = for { d <- Devices if d.price > 1000.0 } yield d.acquisition id: Long price: Double acquisition: Date db.withTransaction { implicit session => (session) val acquisitonDates = query.run no unexpected behavior, no loading strategy configuration, just write code }

  17. 6 Loosely-coupled, flexible mapping

  18. Table description class class Devices(tag: Tag) Devices(tag: Tag) extends extends Table[ def def id = column[Long] ( id = column[Long] ("ID" def def price = column[Double]( price = column[Double]("PRICE" def def acquisition = column[Date] ( acquisition = column[Date] ("ACQUISITION" def def * = * = (id, price, acquisition) (id, price, acquisition) Table[(Long, Double, Date (Long, Double, Date) )]( ](tag, tag,"DEVICES "DEVICES" ") { "ID", , O O. .PrimaryKey PrimaryKey) ) "PRICE") ) "ACQUISITION") ) ) { } val val Devices Devices = = TableQuery TableQuery[Devices] [Devices]

  19. Table description class class Devices(tag: Tag) Devices(tag: Tag) extends extends Table[ Table[Long :: Double :: Date :: Long :: Double :: Date :: HNil def def id = column[Long] ( id = column[Long] ("ID" def def price = column[Double]( price = column[Double]("PRICE" def def acquisition = column[Date] ( acquisition = column[Date] ("ACQUISITION" def def * = * = id id :: price :: acquisition :: :: price :: acquisition :: HNil HNil) )]( , O O. .PrimaryKey PrimaryKey) ) "PRICE") ) "ACQUISITION") ) HNil ](tag, tag,"DEVICES "DEVICES" ") { ) { "ID", } val val Devices Devices = = TableQuery TableQuery[Devices] [Devices]

  20. case class mapping case case class price price: Double, : Double, acquisition acquisition: Date) class Device Device( (id id: : Long, Long, : Date) class class Devices(tag: Tag) Devices(tag: Tag) extends extends Table[ def def id = column[Long] ( id = column[Long] ("ID" def def price = column[Double]( price = column[Double]("PRICE" def def acquisition = column[Date] ( acquisition = column[Date] ("ACQUISITION" = (id, price, acquisition) <> ( (Device.tupled,Device.unapply Device.tupled,Device.unapply) ) } val val Devices Devices = = TableQuery TableQuery[Devices] [Devices] Table[Device Device]( ](tag, tag,"DEVICES "DEVICES" ") { ) { "ID", "PRICE") ) "ACQUISITION") ) <> , O O. .PrimaryKey PrimaryKey) ) def def * * = (id, price, acquisition)

  21. Custom mapping def def construct construct : (( def def extract extract: : ((Long,Double,Date Long,Double,Date)) => : CustomType CustomType => Option[( => Option[(Long,Double,Date )) => CustomType CustomType Long,Double,Date)] )] class class Devices(tag: Tag) Devices(tag: Tag) extends extends Table[ def def id = column[Long] ( id = column[Long] ("ID" def def price = column[Double]( price = column[Double]("PRICE" def def acquisition = column[Date] ( acquisition = column[Date] ("ACQUISITION" * = (id, price, acquisition) <> ( (construct,extract construct,extract) ) } val val Devices Devices = = TableQuery TableQuery[Devices] [Devices] Table[CustomType CustomType]( ](tag, tag,"DEVICES "DEVICES" ") { "ID", "PRICE") ) "ACQUISITION") ) <> ) { , O O. .PrimaryKey PrimaryKey) ) def def * = (id, price, acquisition)

  22. 7 Plain SQL support

  23. Plain SQL support import scala.slick.jdbc.{GetResult, StaticQuery} import StaticQuery.interpolation implicit val getDeviceResult = GetResult(r => Device(r.<<, r.<<, r.<<)) val price = 1000.0 val expensiveDevices: List[Device] = sql"select * from DEVICES where PRICE > $price" .as[Device].list

  24. 8 composable / re-usable queries

  25. Composable, re-usable queries def deviceLocations (companies: Query[Companies,Company]) : Query[Column[String],String] = { companies.computers.devices.sites.map(_.location) } re-use joins re-use queries val apples = Companies.filter(_.name iLike "%apple%") val locations : Seq[String] = { deviceLocations(apples) .filter(_.inAmerica: Column[String]=>Column[Boolean]) .run } re-use user-defined operators execute exactly one, precise query

  26. Live Demo

  27. Slick app design

  28. Mental paradigm shift Non-composable executor APIs (DAOs) DevicesDAO .inPriceRange( 500.0, 2000.0 ) : List[Device] executes Composable query libraries devices .inPriceRange( 500.0, 2000.0 ) : Query[_,Device] composes

  29. Suggested Slick app architecture View Controller Database Session Table classes Composable Query Library Non-composable Executor API / DAO Companies Computers Devices Sites def byId( Column[Long] ) : Query[ ,Computers] def byId( id:Long ) : Device def withComputers : Query[ ,( ,Computers)] def withComputers : Map[ ,Seq[Computer]] def iLike( Column[String] ) : Column[Boolean]

  30. Relationships / Associations Via composable queries using foreign keys! companies.withComputers : Query[ ,(Company,Computer)] Not object references within query results Not executor APIs

  31. Auto joins (not in Slick, but easy to implement) implicitdef autojoin1 = joinCondition[Sites,Devices] implicitdef autojoin2 = joinCondition[Devices,Computers] (_.computerId === _.id) (_.id === _.siteId) sites.autoJoin(devices).further(computers) : Query[_,(Site,Computer)] sites.autoJoin(devices).autoJoinVia(computers)(_._2) : Query[_,((Site,Device),Computer)] Device Computer Site id: Long price: Double acquisition: Date siteId: Long Id: Long Name: String companyId: Int id: Long name: String 1 n n 1

  32. Other features

  33. Other features inserts += ++=, updates query.update( ) user defined column types, e.g. type-safe ids user defined database functions

  34. Outlook

  35. 2.0 until end of 2013 code-generation based type providers hlists and custom shapes (no 22-col limit, easy integration with shapeless, etc.) distributed queries (over multiple dbs) improved pre-compiled queries

  36. Current experiments improved macro-based api (simpler types) macro-based type providers schema manipulation api migration/version management tool extended for-comprehensions (order, group) Thanks to @amirsh @clhodapp @nafg

  37. Thank you Scala User Group Berlin Brandenburg slick.typesafe.com @cvogt @StefanZeiger http://slick.typesafe.com/talks/ https://github.com/cvogt/slick-presentation/tree/2013/sug-berlin

More Related Content