so slick! an introduction
DESCRIPTION
So Slick! An introduction. Jan Christopher Vogt, EPFL Slick Team. Scala User Group Berlin Brandenburg. (vs. ORM). Functional -Relational Mapper natural fit (no impedance mismatch ) declarative embraces relational stateless - PowerPoint PPT PresentationTRANSCRIPT
![Page 1: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/1.jpg)
So Slick!An introduction
Jan Christopher Vogt, EPFLSlick Team
Scala User GroupBerlin Brandenburg
![Page 2: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/2.jpg)
(vs. ORM)
• Functional-Relational Mapper• natural fit (no impedance mismatch)• declarative• embraces relational• stateless• Slick is to ORM what Scala is to Java
![Page 3: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/3.jpg)
8 Reasons for using Slick
![Page 4: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/4.jpg)
1Scala collection-like
API
![Page 5: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/5.jpg)
Scala collection-like API
for ( d <- Devices; if d.price > 1000.0) yield d.acquisition
Devices .filter(_.price > 1000.0) .map(_.acquisition)
Deviceid: Longprice: Doubleacquisition: Date
![Page 6: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/6.jpg)
2Predictable SQL structure
![Page 7: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/7.jpg)
Predictable SQL structure
Devices .filter(_.price > 1000.0) .map(_.acquisition) .selectStatement
select x2."ACQUISITION" from "DEVICE" x2 where x2."PRICE" > 1000.0
![Page 8: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/8.jpg)
3Type-safety
![Page 9: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/9.jpg)
Compile-Time Safety
• Spelling mistake in column name?• Wrong column type?• Query doesn’t match expected result type?
scalac sees it all!
![Page 10: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/10.jpg)
• Error messages can destroy the illusion
Caution: Error messages can be bad
![Page 11: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/11.jpg)
Enforce schema consistency
• Generate DDL from table classes• Slick 2.0: Generate table classes and mapped
classes from database
![Page 12: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/12.jpg)
4Small configuration
using Scala code
![Page 13: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/13.jpg)
Table description
class Devices(tag: Tag) extends Table[(Long, Double, Date)](tag,"DEVICES") { def id = column[Long] ("ID", O.PrimaryKey) def price = column[Double]("PRICE") def acquisition = column[Date] ("ACQUISITION") def * = (id, price, acquisition)
}def Devices = TableQuery[Devices]
can be auto-generated in Slick 2.0
![Page 14: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/14.jpg)
Connectimport scala.slick.driver.H2Driver.simple._
val db = Database.forURL( "jdbc:h2:mem:testdb", "org.h2.Driver")
db.withTransaction { implicit session =>
// <- run queries here
}
![Page 15: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/15.jpg)
5Explicit control over
execution and transfer
![Page 16: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/16.jpg)
Execution controlval query = for { d <- Devices if d.price > 1000.0 } yield d.acquisition
db.withTransaction { implicit session =>
val acquisitonDates = query.run
}
Deviceid: Longprice: Doubleacquisition: Date
(session)
no unexpected behavior,no loading strategy configuration,just write code
![Page 17: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/17.jpg)
6Loosely-coupled, flexible mapping
![Page 18: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/18.jpg)
Table description
class Devices(tag: Tag) extends Table[(Long, Double, Date)](tag,"DEVICES") { def id = column[Long] ("ID", O.PrimaryKey) def price = column[Double]("PRICE") def acquisition = column[Date] ("ACQUISITION") def * = (id, price, acquisition)
}val Devices = TableQuery[Devices]
![Page 19: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/19.jpg)
Table description
class Devices(tag: Tag)extends Table[Long :: Double :: Date :: HNil)](tag,"DEVICES") { def id = column[Long] ("ID", O.PrimaryKey) def price = column[Double]("PRICE") def acquisition = column[Date] ("ACQUISITION") def * = id :: price :: acquisition :: HNil
}val Devices = TableQuery[Devices]
![Page 20: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/20.jpg)
case class mapping
class Devices(tag: Tag) extends Table[Device](tag,"DEVICES") { def id = column[Long] ("ID", O.PrimaryKey) def price = column[Double]("PRICE") def acquisition = column[Date] ("ACQUISITION") def * = (id, price, acquisition) <> (Device.tupled,Device.unapply)}val Devices = TableQuery[Devices]
case class Device(id: Long, price: Double, acquisition: Date)
![Page 21: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/21.jpg)
Custom mapping
class Devices(tag: Tag) extends Table[CustomType](tag,"DEVICES") { def id = column[Long] ("ID", O.PrimaryKey) def price = column[Double]("PRICE") def acquisition = column[Date] ("ACQUISITION") def * = (id, price, acquisition) <> (construct,extract)}val Devices = TableQuery[Devices]
def construct : ((Long,Double,Date)) => CustomTypedef extract: CustomType => Option[(Long,Double,Date)]
![Page 22: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/22.jpg)
7Plain SQL support
![Page 23: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/23.jpg)
Plain SQL supportimport 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
![Page 24: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/24.jpg)
8composable /
re-usable queries
![Page 25: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/25.jpg)
Composable, re-usable queriesdef deviceLocations (companies: Query[Companies,Company]) : Query[Column[String],String] = { companies.computers.devices.sites.map(_.location)}
val apples = Companies.filter(_.name iLike "%apple%")val locations : Seq[String] = { deviceLocations(apples) .filter(_.inAmerica: Column[String]=>Column[Boolean]) .run}
re-use queries
re-use user-defined operators
re-use joins
execute exactly one, precise query
![Page 26: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/26.jpg)
Live Demo
![Page 27: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/27.jpg)
Slick app design
![Page 28: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/28.jpg)
Mental paradigm shift
Non-composable executor APIs (DAOs) DevicesDAO .inPriceRange( 500.0, 2000.0 ) : List[Device]
Composable query libraries devices .inPriceRange( 500.0, 2000.0 ) : Query[_,Device]
executes
composes
![Page 29: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/29.jpg)
Non-composableExecutor API / DAOdef byId( id:Long ) : Device
def withComputers : Map[…,Seq[Computer]]
…
ComposableQuery Librarydef byId( Column[Long] ) : Query[…,Computers]
def withComputers : Query[…,(…,Computers)]
def iLike( Column[String] ) : Column[Boolean]
…
Table classes
CompaniesComputersDevicesSites…
Controller Database SessionView
Suggested Slick app architecture
![Page 30: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/30.jpg)
Relationships / Associations
• Via composable queries using foreign keys!
companies.withComputers : Query[…,(Company,Computer)]
• Not object references within query results• Not executor APIs
![Page 31: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/31.jpg)
Auto joins(not in Slick, but easy to implement)implicit def autojoin1 = joinCondition[Sites,Devices] (_.id === _.siteId)implicit def autojoin2 = joinCondition[Devices,Computers] (_.computerId === _.id)
sites.autoJoin(devices).further(computers) : Query[_,(Site,Computer)]sites.autoJoin(devices).autoJoinVia(computers)(_._2) : Query[_,((Site,Device),Computer)]
Site
id: Longname: String
Device
id: Longprice: Doubleacquisition: DatesiteId: Long
1n
Computer
Id: LongName: StringcompanyId: Int
1 n
![Page 32: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/32.jpg)
Other features
![Page 33: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/33.jpg)
Other features
• inserts += ++=, updates query.update(…)• user defined column types, e.g. type-safe ids• user defined database functions• …
![Page 34: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/34.jpg)
Outlook
![Page 35: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/35.jpg)
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
![Page 36: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/36.jpg)
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
![Page 37: So Slick! An introduction](https://reader035.vdocuments.us/reader035/viewer/2022062305/5681623c550346895dd271f8/html5/thumbnails/37.jpg)
slick.typesafe.com
@cvogt@StefanZeiger
http://slick.typesafe.com/talks/https://github.com/cvogt/slick-presentation/tree/2013/sug-berlin
Thank youScala User Group
Berlin Brandenburg