SlideShare a Scribd company logo
1 of 39
Patterns for Slick database
applications
Jan Christopher Vogt, EPFL
Slick Team

#scalax
Recap: What is Slick?
(for ( c <- coffees;
if c.sales > 999
) yield c.name).run

select "COF_NAME"
from
"COFFEES"
where "SALES" > 999
Agenda
• Query composition and re-use
• Getting rid of boiler plate in Slick 2.0
–
–
–

outer joins / auto joins
auto increment
code generation

• Dynamic Slick queries
Query composition and reuse
For-expression desugaring in Scala
for ( c <- coffees;
if c.sales > 999
) yield c.name

coffees
.withFilter(_.sales > 999)
.map(_.name)
Types in Slick
class Coffees(tag: Tag) extends Table[C](tag,"COFFEES”) {
def * = (name, supId, price, sales, total) <> ...
val name = column[String]("COF_NAME", O.PrimaryKey)
val supId = column[Int]("SUP_ID")
val price = column[BigDecimal]("PRICE")
val sales = column[Int]("SALES")
val total = column[Int]("TOTAL")
}
lazy val coffees = TableQuery[Coffees]

<: Query[Coffees,C]

coffees.map(c => c.name)
(coffees:TableQuery[Coffees,_]).map(
(coffees
).map(
(c:Coffees) => (c.name Column[String])
(c.name:
(c
)
)
): Query[Column[String],String]
)
Table extensions
class Coffees(tag: Tag) extends Table[C](tag,"COFFEES”) {
…
val price = column[BigDecimal]("PRICE")
val sales = column[Int]("SALES")
def

revenue = price.asColumnOf[Double] *
sales.asColumnOf[Double]

}

coffees.map(c => c.revenue)
Query extensions
implicit class QueryExtensions[T,E]
( val q: Query[T,E] ){
def page(no: Int, pageSize: Int = 10)
: Query[T,E]
= q.drop( (no-1)*pageSize ).take(pageSize)
}
suppliers.page(5)
coffees.sortBy(_.name).page(5)
Query extensions by Table
implicit class CoffeesExtensions
( val q: Query[Coffees,C] ){
def byName( name: Column[String] )
: Query[Coffees,C]
= q.filter(_.name === name).sortBy(_.name)
}
coffees.byName("ColumbianDecaf").page(5)
Query extensions for joins
implicit class CoffeesExtensions2( val q: Query[Coffees,C] ){
def withSuppliers
(s: Query[Suppliers,S] = Tables.suppliers)
: Query[(Coffees,Suppliers),(C,S)]
= q.join(s).on(_.supId===_.id)
def suppliers
(s: Query[Suppliers, S] = Tables.suppliers)
: Query[Suppliers, S]
= q.withSuppliers(s).map(_._2)
}
coffees.withSuppliers() : Query[(Coffees,Suppliers),(C,S)
coffees.withSuppliers( suppliers.filter(_.city === "Henderson") )
// buyable coffees
coffeeShops.coffees().suppliers().withCoffees()
Query extensions by Interface
trait HasSuppliers{ def supId: Column[Int] }
class Coffees(…)
extends Table... with HasSuppliers {…}
class CofInventory(…) extends Table... with HasSuppliers {…}
implicit class HasSuppliersExtensions[T <: HasSupplier,E]
( val q: Query[T,E] ){
def bySupId(id: Column[Int]): Query[T,E]
= q.filter( _.supId === id )
def withSuppliers
(s: Query[Suppliers,S] = Tables.suppliers)
: Query[(T,Suppliers),(E,S)]
= q.join(s).on(_.supId===_.id)
def suppliers ...
}
// available quantities of coffees
cofInventory.withSuppliers()
.map{ case (i,s) =>
i.quantity.asColumnOf[String] ++ " of " ++ i.cofName ++ " at " ++ s.name
}
Query extensions summary
• Mindshift required!
Think code, not monolithic query strings.
• Stay completely lazy!
Keep Query[…]s as long as you can.
• Re-use!
Write query functions or extensions
methods for shorter, better and DRY code.
Getting rid of boilerplate
Auto joins
Auto joins
implicit class QueryExtensions2[T,E]
( val q: Query[T,E] ){
def autoJoin[T2,E2]
( q2:Query[T2,E2] )
( implicit condition: (T,T2) => Column[Boolean] )
: Query[(T,T2),(E,E2)]
= q.join(q2).on(condition)
}
implicit def joinCondition1
= (c:Coffees,s:Suppliers) => c.supId === s.id
coffees.autoJoin( suppliers ) : Query[(Coffees,Suppliers),(C,S)]
coffees.autoJoin( suppliers ).map(_._2).autoJoin(cofInventory)
Auto incrementing inserts
Auto incrementing inserts
val supplier
= Supplier( 0, "Arabian Coffees Inc.", ... )
// now ignores auto-increment column
suppliers.insert( supplier )
// includes auto-increment column
suppliers.forceInsert( supplier )
Code generatION
Code generator for Slick code
// runner for default config
import scala.slick.meta.codegen.SourceCodeGenerator
SourceCodeGenerator.main(
Array(
"scala.slick.driver.H2Driver",
"org.h2.Driver",
"jdbc:h2:mem:test",
"src/main/scala/", // base src folder
"demo" // package
)
)
Generated code
package demo
object Tables extends {
val profile = scala.slick.driver.H2Driver
} with Tables
trait Tables {
val profile: scala.slick.driver.JdbcProfile
import profile.simple._
case class CoffeeRow(name: String, supId: Int, ...)
implicit def GetCoffees
= GetResult{r => CoffeeRow.tupled((r.<<, ... )) }
class Coffees(tag: Tag) extends Table[CoffeeRow](…){…}
...
OUTER JOINS
Outer join limitation in Slick
suppliers.leftJoin(coffees)
.on(_.id === _.supId)
.run // SlickException: Read NULL value ...
id

name

name

supId

1

Superior Coffee

NULL

NULL

2

Acme, Inc.

Colombian

2

2

Acme, Inc.

French_Roast

2

LEFT JOIN
id

name

name

supId

1

Superior Coffee

Colombian

2

2

Acme, Inc.

French_Roast

2
Outer join pattern
suppliers.leftJoin(coffees)
.on(_.id === _.supId)
.map{ case(s,c) => (s,(c.name.?,c.supId.?,…)) }
.run
.map{ case (s,c) =>
(s,c._1.map(_ => Coffee(c._1.get,c._2.get,…))) }
// Generated outer join helper
suppliers.leftJoin(coffees)
.on(_.id === _.supId)
.map{ case(s,c) => (s,c.?) }
.run
CUSTOMIZABLE Code
generatION
Using code generator as a library
val metaModel = db.withSession{ implicit session =>
profile.metaModel // e.g. H2Driver.metaModel
}
import scala.slick.meta.codegen.SourceCodeGenerator
val codegen = new SourceCodeGenerator(metaModel){
// <- customize here
}
codegen.writeToFile(
profile = "scala.slick.driver.H2Driver”,
folder = "src/main/scala/",
pkg = "demo",
container = "Tables",
fileName="Tables.scala" )
Adjust name mapping
import scala.slick.util.StringExtensions._
val codegen =
new SourceCodeGenerator(metaModel){
override def tableName
= _.toLowerCase.toCamelCase
override def entityName
= tableName(_).dropRight(1)
}
Generate auto-join conditions 1
class CustomizedCodeGenerator(metaModel: Model)
extends SourceCodeGenerator(metaModel){
override def code = {
super.code + "nn" + s"""
/** implicit join conditions for auto joins */
object AutoJoins{
${indent(joins.mkString("n"))}
}
""".trim()
}
…
Generate auto-join conditions 2
…
val joins = tables.flatMap( _.foreignKeys.map{ foreignKey =>
import foreignKey._
val fkt = referencingTable.tableClassName
val pkt = referencedTable.tableClassName
val columns = referencingColumns.map(_.name) zip
referencedColumns.map(_.name)
s"implicit def autojoin${name.capitalize} "+
" = (left:${fkt},right:${pkt}) => " +
columns.map{
case (lcol,rcol) =>
"left."+lcol + " === " + "right."+rcol
}.mkString(" && ")
}
Other uses of Slick code generation
• Glue code (Play, etc.)
• n-n join code
• Migrating databases (warning: types
change)
(generate from MySQL, create in
Postgres)
• Generate repetitive regarding data model
(aka model driven software engineering)
• Generate DDL for external model
Use code generation wisely
• Don’t loose language-level abstraction
• Add your generator and data model to
version control
• Complete but new and therefor
experimental in Slick
Dynamic Queries
Common use case for web apps
Dynamically decide
• displayed columns
• filter conditions
• sort columns / order
Dynamic column
class Coffees(tag: Tag)
extends Table[CoffeeRow](…){
val name = column[String]("COF_NAME",…)
}

coffees.map(c => c.name)
coffees.map(c =>
c.column[String]("COF_NAME")
)

Be careful about security!
Example: sortDynamic

suppliers.sortDynamic("street.desc,city.desc")
sortDynamic 1
implicit class QueryExtensions3[E,T<: Table[E]]
( val query: Query[T,E] ){
def sortDynamic(sortString: String) : Query[T,E] = {
// split string into useful pieces
val sortKeys = sortString.split(',').toList.map(
_.split('.').map(_.toUpperCase).toList )
sortDynamicImpl(sortKeys)
}
private def sortDynamicImpl(sortKeys: List[Seq[String]]) = ???
}

suppliers.sortDynamic("street.desc,city.desc")
sortDynamic 2
...
private def sortDynamicImpl(sortKeys: List[Seq[String]]) : Query[T,E] = {
sortKeys match {
case key :: tail =>
sortDynamicImpl( tail ).sortBy( table =>
key match {
case name :: Nil =>
table.column[String](name).asc
case name :: "ASC" :: Nil => table.column[String](name).asc
case name :: "DESC" :: Nil => table.column[String](name).desc
case o => throw new Exception("invalid sorting key: "+o)
}
)
case Nil => query
}
}
}
suppliers.sortDynamic("street.desc,city.desc")
Summary
• Query composition and re-use
• Getting rid of boiler plate in Slick 2.0
–
–
–

outer joins / auto joins
auto increment
code generation

• Dynamic Slick queries
Thank you

#scalax
slick.typesafe.com
@cvogt
http://slick.typesafe.com/talks/
https://github.com/cvogt/slick-presentation/tree/scala-exchange-2013
filterDynamic
coffees.filterDynamic("COF_NAME like Decaf")

More Related Content

What's hot

SQLチューニング入門 入門編
SQLチューニング入門 入門編SQLチューニング入門 入門編
SQLチューニング入門 入門編
Miki Shimogai
 
シェル芸初心者によるシェル芸入門
シェル芸初心者によるシェル芸入門シェル芸初心者によるシェル芸入門
シェル芸初心者によるシェル芸入門
icchy
 

What's hot (20)

Caching ガイダンスの話
Caching ガイダンスの話Caching ガイダンスの話
Caching ガイダンスの話
 
OSを手作りするという趣味と仕事
OSを手作りするという趣味と仕事OSを手作りするという趣味と仕事
OSを手作りするという趣味と仕事
 
SQLチューニング入門 入門編
SQLチューニング入門 入門編SQLチューニング入門 入門編
SQLチューニング入門 入門編
 
3週連続DDDその3 ドメイン駆動設計 戦略的設計
3週連続DDDその3  ドメイン駆動設計 戦略的設計3週連続DDDその3  ドメイン駆動設計 戦略的設計
3週連続DDDその3 ドメイン駆動設計 戦略的設計
 
nginx入門
nginx入門nginx入門
nginx入門
 
ファントム異常を排除する高速なトランザクション処理向けインデックス
ファントム異常を排除する高速なトランザクション処理向けインデックスファントム異常を排除する高速なトランザクション処理向けインデックス
ファントム異常を排除する高速なトランザクション処理向けインデックス
 
Ruby で高速なプログラムを書く
Ruby で高速なプログラムを書くRuby で高速なプログラムを書く
Ruby で高速なプログラムを書く
 
クソコード動画「Managerクラス」解説
クソコード動画「Managerクラス」解説クソコード動画「Managerクラス」解説
クソコード動画「Managerクラス」解説
 
EventStormingワークショップ 〜かつてない図書館をモデリングしてみよう〜
EventStormingワークショップ 〜かつてない図書館をモデリングしてみよう〜EventStormingワークショップ 〜かつてない図書館をモデリングしてみよう〜
EventStormingワークショップ 〜かつてない図書館をモデリングしてみよう〜
 
きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回きつねさんでもわかるLlvm読書会 第2回
きつねさんでもわかるLlvm読書会 第2回
 
プランニングポーカーで学ぶ相対見積
プランニングポーカーで学ぶ相対見積プランニングポーカーで学ぶ相対見積
プランニングポーカーで学ぶ相対見積
 
Elasticsearchのサジェスト機能を使った話
Elasticsearchのサジェスト機能を使った話Elasticsearchのサジェスト機能を使った話
Elasticsearchのサジェスト機能を使った話
 
【BS13】チーム開発がこんなにも快適に!コーディングもデバッグも GitHub 上で。 GitHub Codespaces で叶えられるシームレスな開発
【BS13】チーム開発がこんなにも快適に!コーディングもデバッグも GitHub 上で。 GitHub Codespaces で叶えられるシームレスな開発【BS13】チーム開発がこんなにも快適に!コーディングもデバッグも GitHub 上で。 GitHub Codespaces で叶えられるシームレスな開発
【BS13】チーム開発がこんなにも快適に!コーディングもデバッグも GitHub 上で。 GitHub Codespaces で叶えられるシームレスな開発
 
わしわし的おすすめ .gitconfig 設定 (と見せかけて実はみんなのおすすめ .gitconfig 設定を教えてもらう魂胆) #広島Git 勉強会
わしわし的おすすめ  .gitconfig 設定 (と見せかけて実はみんなのおすすめ .gitconfig 設定を教えてもらう魂胆) #広島Git 勉強会わしわし的おすすめ  .gitconfig 設定 (と見せかけて実はみんなのおすすめ .gitconfig 設定を教えてもらう魂胆) #広島Git 勉強会
わしわし的おすすめ .gitconfig 設定 (と見せかけて実はみんなのおすすめ .gitconfig 設定を教えてもらう魂胆) #広島Git 勉強会
 
第16回Lucene/Solr勉強会 – ランキングチューニングと定量評価 #SolrJP
第16回Lucene/Solr勉強会 – ランキングチューニングと定量評価 #SolrJP第16回Lucene/Solr勉強会 – ランキングチューニングと定量評価 #SolrJP
第16回Lucene/Solr勉強会 – ランキングチューニングと定量評価 #SolrJP
 
DeviceOwnerのお話
DeviceOwnerのお話DeviceOwnerのお話
DeviceOwnerのお話
 
シェル芸初心者によるシェル芸入門
シェル芸初心者によるシェル芸入門シェル芸初心者によるシェル芸入門
シェル芸初心者によるシェル芸入門
 
本当にあったApache Spark障害の話
本当にあったApache Spark障害の話本当にあったApache Spark障害の話
本当にあったApache Spark障害の話
 
If文から機械学習への道
If文から機械学習への道If文から機械学習への道
If文から機械学習への道
 
『データベースのどこが問題?』を時間かけずにレスポンス・タイム分析(RTA)!
『データベースのどこが問題?』を時間かけずにレスポンス・タイム分析(RTA)!『データベースのどこが問題?』を時間かけずにレスポンス・タイム分析(RTA)!
『データベースのどこが問題?』を時間かけずにレスポンス・タイム分析(RTA)!
 

Viewers also liked

Domain Driven Design Experience Report
Domain Driven Design Experience ReportDomain Driven Design Experience Report
Domain Driven Design Experience Report
Skills Matter
 
Akka in Practice: Designing Actor-based Applications
Akka in Practice: Designing Actor-based ApplicationsAkka in Practice: Designing Actor-based Applications
Akka in Practice: Designing Actor-based Applications
NLJUG
 

Viewers also liked (16)

Slick: Bringing Scala’s Powerful Features to Your Database Access
Slick: Bringing Scala’s Powerful Features to Your Database Access Slick: Bringing Scala’s Powerful Features to Your Database Access
Slick: Bringing Scala’s Powerful Features to Your Database Access
 
Slick 3.0 functional programming and db side effects
Slick 3.0   functional programming and db side effectsSlick 3.0   functional programming and db side effects
Slick 3.0 functional programming and db side effects
 
Reactive Database Access With Slick 3
Reactive Database Access With Slick 3Reactive Database Access With Slick 3
Reactive Database Access With Slick 3
 
Slick: A control plane for middleboxes
Slick: A control plane for middleboxesSlick: A control plane for middleboxes
Slick: A control plane for middleboxes
 
Building Scalable, Distributed Job Queues with Redis and Redis::Client
Building Scalable, Distributed Job Queues with Redis and Redis::ClientBuilding Scalable, Distributed Job Queues with Redis and Redis::Client
Building Scalable, Distributed Job Queues with Redis and Redis::Client
 
Domain Driven Design Experience Report
Domain Driven Design Experience ReportDomain Driven Design Experience Report
Domain Driven Design Experience Report
 
Slick – the modern way to access your Data
Slick – the modern way to access your DataSlick – the modern way to access your Data
Slick – the modern way to access your Data
 
実戦Scala
実戦Scala実戦Scala
実戦Scala
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 
Slick - The Structured Way
Slick - The Structured WaySlick - The Structured Way
Slick - The Structured Way
 
CQRS and what it means for your architecture
CQRS and what it means for your architectureCQRS and what it means for your architecture
CQRS and what it means for your architecture
 
Reactive database access with Slick3
Reactive database access with Slick3Reactive database access with Slick3
Reactive database access with Slick3
 
Spark Hands-on
Spark Hands-onSpark Hands-on
Spark Hands-on
 
Architecting Microservices in .Net
Architecting Microservices in .NetArchitecting Microservices in .Net
Architecting Microservices in .Net
 
Akka in Practice: Designing Actor-based Applications
Akka in Practice: Designing Actor-based ApplicationsAkka in Practice: Designing Actor-based Applications
Akka in Practice: Designing Actor-based Applications
 
Modern SQL in Open Source and Commercial Databases
Modern SQL in Open Source and Commercial DatabasesModern SQL in Open Source and Commercial Databases
Modern SQL in Open Source and Commercial Databases
 

Similar to Patterns for slick database applications

Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 

Similar to Patterns for slick database applications (20)

The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 
Rainer Grimm, “Functional Programming in C++11”
Rainer Grimm, “Functional Programming in C++11”Rainer Grimm, “Functional Programming in C++11”
Rainer Grimm, “Functional Programming in C++11”
 
Mock Hell PyCon DE and PyData Berlin 2019
Mock Hell PyCon DE and PyData Berlin 2019Mock Hell PyCon DE and PyData Berlin 2019
Mock Hell PyCon DE and PyData Berlin 2019
 
The Ring programming language version 1.5.2 book - Part 29 of 181
The Ring programming language version 1.5.2 book - Part 29 of 181The Ring programming language version 1.5.2 book - Part 29 of 181
The Ring programming language version 1.5.2 book - Part 29 of 181
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 
The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.10 book - Part 54 of 212The Ring programming language version 1.10 book - Part 54 of 212
The Ring programming language version 1.10 book - Part 54 of 212
 
The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184
 
The Ring programming language version 1.5.3 book - Part 54 of 184
The Ring programming language version 1.5.3 book - Part 54 of 184The Ring programming language version 1.5.3 book - Part 54 of 184
The Ring programming language version 1.5.3 book - Part 54 of 184
 
The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31The Ring programming language version 1.4.1 book - Part 13 of 31
The Ring programming language version 1.4.1 book - Part 13 of 31
 
A Few of My Favorite (Python) Things
A Few of My Favorite (Python) ThingsA Few of My Favorite (Python) Things
A Few of My Favorite (Python) Things
 
The Ring programming language version 1.8 book - Part 50 of 202
The Ring programming language version 1.8 book - Part 50 of 202The Ring programming language version 1.8 book - Part 50 of 202
The Ring programming language version 1.8 book - Part 50 of 202
 
The Ring programming language version 1.4 book - Part 8 of 30
The Ring programming language version 1.4 book - Part 8 of 30The Ring programming language version 1.4 book - Part 8 of 30
The Ring programming language version 1.4 book - Part 8 of 30
 
The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210The Ring programming language version 1.9 book - Part 53 of 210
The Ring programming language version 1.9 book - Part 53 of 210
 
R programming language
R programming languageR programming language
R programming language
 
The Ring programming language version 1.5.3 book - Part 40 of 184
The Ring programming language version 1.5.3 book - Part 40 of 184The Ring programming language version 1.5.3 book - Part 40 of 184
The Ring programming language version 1.5.3 book - Part 40 of 184
 
The Ring programming language version 1.5.1 book - Part 43 of 180
The Ring programming language version 1.5.1 book - Part 43 of 180The Ring programming language version 1.5.1 book - Part 43 of 180
The Ring programming language version 1.5.1 book - Part 43 of 180
 
The Ring programming language version 1.5.1 book - Part 28 of 180
The Ring programming language version 1.5.1 book - Part 28 of 180The Ring programming language version 1.5.1 book - Part 28 of 180
The Ring programming language version 1.5.1 book - Part 28 of 180
 
The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84The Ring programming language version 1.2 book - Part 32 of 84
The Ring programming language version 1.2 book - Part 32 of 84
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
 

More from Skills Matter

Oscar reiken jr on our success at manheim
Oscar reiken jr on our success at manheimOscar reiken jr on our success at manheim
Oscar reiken jr on our success at manheim
Skills Matter
 
Russ miles-cloudfoundry-deep-dive
Russ miles-cloudfoundry-deep-diveRuss miles-cloudfoundry-deep-dive
Russ miles-cloudfoundry-deep-dive
Skills Matter
 
I went to_a_communications_workshop_and_they_t
I went to_a_communications_workshop_and_they_tI went to_a_communications_workshop_and_they_t
I went to_a_communications_workshop_and_they_t
Skills Matter
 

More from Skills Matter (20)

5 things cucumber is bad at by Richard Lawrence
5 things cucumber is bad at by Richard Lawrence5 things cucumber is bad at by Richard Lawrence
5 things cucumber is bad at by Richard Lawrence
 
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
Scala e xchange 2013 haoyi li on metascala a tiny diy jvmScala e xchange 2013 haoyi li on metascala a tiny diy jvm
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
 
Oscar reiken jr on our success at manheim
Oscar reiken jr on our success at manheimOscar reiken jr on our success at manheim
Oscar reiken jr on our success at manheim
 
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
 
Cukeup nyc ian dees on elixir, erlang, and cucumberl
Cukeup nyc ian dees on elixir, erlang, and cucumberlCukeup nyc ian dees on elixir, erlang, and cucumberl
Cukeup nyc ian dees on elixir, erlang, and cucumberl
 
Cukeup nyc peter bell on getting started with cucumber.js
Cukeup nyc peter bell on getting started with cucumber.jsCukeup nyc peter bell on getting started with cucumber.js
Cukeup nyc peter bell on getting started with cucumber.js
 
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
 
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
 
Progressive f# tutorials nyc don syme on keynote f# in the open source world
Progressive f# tutorials nyc don syme on keynote f# in the open source worldProgressive f# tutorials nyc don syme on keynote f# in the open source world
Progressive f# tutorials nyc don syme on keynote f# in the open source world
 
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
 
Dmitry mozorov on code quotations code as-data for f#
Dmitry mozorov on code quotations code as-data for f#Dmitry mozorov on code quotations code as-data for f#
Dmitry mozorov on code quotations code as-data for f#
 
A poet's guide_to_acceptance_testing
A poet's guide_to_acceptance_testingA poet's guide_to_acceptance_testing
A poet's guide_to_acceptance_testing
 
Russ miles-cloudfoundry-deep-dive
Russ miles-cloudfoundry-deep-diveRuss miles-cloudfoundry-deep-dive
Russ miles-cloudfoundry-deep-dive
 
Serendipity-neo4j
Serendipity-neo4jSerendipity-neo4j
Serendipity-neo4j
 
Simon Peyton Jones: Managing parallelism
Simon Peyton Jones: Managing parallelismSimon Peyton Jones: Managing parallelism
Simon Peyton Jones: Managing parallelism
 
Plug 20110217
Plug   20110217Plug   20110217
Plug 20110217
 
Lug presentation
Lug presentationLug presentation
Lug presentation
 
I went to_a_communications_workshop_and_they_t
I went to_a_communications_workshop_and_they_tI went to_a_communications_workshop_and_they_t
I went to_a_communications_workshop_and_they_t
 
Plug saiku
Plug   saikuPlug   saiku
Plug saiku
 
Huguk lily
Huguk lilyHuguk lily
Huguk lily
 

Recently uploaded

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Recently uploaded (20)

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
Exploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with MilvusExploring Multimodal Embeddings with Milvus
Exploring Multimodal Embeddings with Milvus
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 

Patterns for slick database applications

  • 1. Patterns for Slick database applications Jan Christopher Vogt, EPFL Slick Team #scalax
  • 2. Recap: What is Slick? (for ( c <- coffees; if c.sales > 999 ) yield c.name).run select "COF_NAME" from "COFFEES" where "SALES" > 999
  • 3. Agenda • Query composition and re-use • Getting rid of boiler plate in Slick 2.0 – – – outer joins / auto joins auto increment code generation • Dynamic Slick queries
  • 5. For-expression desugaring in Scala for ( c <- coffees; if c.sales > 999 ) yield c.name coffees .withFilter(_.sales > 999) .map(_.name)
  • 6. Types in Slick class Coffees(tag: Tag) extends Table[C](tag,"COFFEES”) { def * = (name, supId, price, sales, total) <> ... val name = column[String]("COF_NAME", O.PrimaryKey) val supId = column[Int]("SUP_ID") val price = column[BigDecimal]("PRICE") val sales = column[Int]("SALES") val total = column[Int]("TOTAL") } lazy val coffees = TableQuery[Coffees] <: Query[Coffees,C] coffees.map(c => c.name) (coffees:TableQuery[Coffees,_]).map( (coffees ).map( (c:Coffees) => (c.name Column[String]) (c.name: (c ) ) ): Query[Column[String],String] )
  • 7. Table extensions class Coffees(tag: Tag) extends Table[C](tag,"COFFEES”) { … val price = column[BigDecimal]("PRICE") val sales = column[Int]("SALES") def revenue = price.asColumnOf[Double] * sales.asColumnOf[Double] } coffees.map(c => c.revenue)
  • 8. Query extensions implicit class QueryExtensions[T,E] ( val q: Query[T,E] ){ def page(no: Int, pageSize: Int = 10) : Query[T,E] = q.drop( (no-1)*pageSize ).take(pageSize) } suppliers.page(5) coffees.sortBy(_.name).page(5)
  • 9. Query extensions by Table implicit class CoffeesExtensions ( val q: Query[Coffees,C] ){ def byName( name: Column[String] ) : Query[Coffees,C] = q.filter(_.name === name).sortBy(_.name) } coffees.byName("ColumbianDecaf").page(5)
  • 10. Query extensions for joins implicit class CoffeesExtensions2( val q: Query[Coffees,C] ){ def withSuppliers (s: Query[Suppliers,S] = Tables.suppliers) : Query[(Coffees,Suppliers),(C,S)] = q.join(s).on(_.supId===_.id) def suppliers (s: Query[Suppliers, S] = Tables.suppliers) : Query[Suppliers, S] = q.withSuppliers(s).map(_._2) } coffees.withSuppliers() : Query[(Coffees,Suppliers),(C,S) coffees.withSuppliers( suppliers.filter(_.city === "Henderson") ) // buyable coffees coffeeShops.coffees().suppliers().withCoffees()
  • 11. Query extensions by Interface trait HasSuppliers{ def supId: Column[Int] } class Coffees(…) extends Table... with HasSuppliers {…} class CofInventory(…) extends Table... with HasSuppliers {…} implicit class HasSuppliersExtensions[T <: HasSupplier,E] ( val q: Query[T,E] ){ def bySupId(id: Column[Int]): Query[T,E] = q.filter( _.supId === id ) def withSuppliers (s: Query[Suppliers,S] = Tables.suppliers) : Query[(T,Suppliers),(E,S)] = q.join(s).on(_.supId===_.id) def suppliers ... } // available quantities of coffees cofInventory.withSuppliers() .map{ case (i,s) => i.quantity.asColumnOf[String] ++ " of " ++ i.cofName ++ " at " ++ s.name }
  • 12. Query extensions summary • Mindshift required! Think code, not monolithic query strings. • Stay completely lazy! Keep Query[…]s as long as you can. • Re-use! Write query functions or extensions methods for shorter, better and DRY code.
  • 13. Getting rid of boilerplate
  • 15. Auto joins implicit class QueryExtensions2[T,E] ( val q: Query[T,E] ){ def autoJoin[T2,E2] ( q2:Query[T2,E2] ) ( implicit condition: (T,T2) => Column[Boolean] ) : Query[(T,T2),(E,E2)] = q.join(q2).on(condition) } implicit def joinCondition1 = (c:Coffees,s:Suppliers) => c.supId === s.id coffees.autoJoin( suppliers ) : Query[(Coffees,Suppliers),(C,S)] coffees.autoJoin( suppliers ).map(_._2).autoJoin(cofInventory)
  • 17. Auto incrementing inserts val supplier = Supplier( 0, "Arabian Coffees Inc.", ... ) // now ignores auto-increment column suppliers.insert( supplier ) // includes auto-increment column suppliers.forceInsert( supplier )
  • 19. Code generator for Slick code // runner for default config import scala.slick.meta.codegen.SourceCodeGenerator SourceCodeGenerator.main( Array( "scala.slick.driver.H2Driver", "org.h2.Driver", "jdbc:h2:mem:test", "src/main/scala/", // base src folder "demo" // package ) )
  • 20. Generated code package demo object Tables extends { val profile = scala.slick.driver.H2Driver } with Tables trait Tables { val profile: scala.slick.driver.JdbcProfile import profile.simple._ case class CoffeeRow(name: String, supId: Int, ...) implicit def GetCoffees = GetResult{r => CoffeeRow.tupled((r.<<, ... )) } class Coffees(tag: Tag) extends Table[CoffeeRow](…){…} ...
  • 22. Outer join limitation in Slick suppliers.leftJoin(coffees) .on(_.id === _.supId) .run // SlickException: Read NULL value ... id name name supId 1 Superior Coffee NULL NULL 2 Acme, Inc. Colombian 2 2 Acme, Inc. French_Roast 2 LEFT JOIN id name name supId 1 Superior Coffee Colombian 2 2 Acme, Inc. French_Roast 2
  • 23. Outer join pattern suppliers.leftJoin(coffees) .on(_.id === _.supId) .map{ case(s,c) => (s,(c.name.?,c.supId.?,…)) } .run .map{ case (s,c) => (s,c._1.map(_ => Coffee(c._1.get,c._2.get,…))) } // Generated outer join helper suppliers.leftJoin(coffees) .on(_.id === _.supId) .map{ case(s,c) => (s,c.?) } .run
  • 25. Using code generator as a library val metaModel = db.withSession{ implicit session => profile.metaModel // e.g. H2Driver.metaModel } import scala.slick.meta.codegen.SourceCodeGenerator val codegen = new SourceCodeGenerator(metaModel){ // <- customize here } codegen.writeToFile( profile = "scala.slick.driver.H2Driver”, folder = "src/main/scala/", pkg = "demo", container = "Tables", fileName="Tables.scala" )
  • 26. Adjust name mapping import scala.slick.util.StringExtensions._ val codegen = new SourceCodeGenerator(metaModel){ override def tableName = _.toLowerCase.toCamelCase override def entityName = tableName(_).dropRight(1) }
  • 27. Generate auto-join conditions 1 class CustomizedCodeGenerator(metaModel: Model) extends SourceCodeGenerator(metaModel){ override def code = { super.code + "nn" + s""" /** implicit join conditions for auto joins */ object AutoJoins{ ${indent(joins.mkString("n"))} } """.trim() } …
  • 28. Generate auto-join conditions 2 … val joins = tables.flatMap( _.foreignKeys.map{ foreignKey => import foreignKey._ val fkt = referencingTable.tableClassName val pkt = referencedTable.tableClassName val columns = referencingColumns.map(_.name) zip referencedColumns.map(_.name) s"implicit def autojoin${name.capitalize} "+ " = (left:${fkt},right:${pkt}) => " + columns.map{ case (lcol,rcol) => "left."+lcol + " === " + "right."+rcol }.mkString(" && ") }
  • 29. Other uses of Slick code generation • Glue code (Play, etc.) • n-n join code • Migrating databases (warning: types change) (generate from MySQL, create in Postgres) • Generate repetitive regarding data model (aka model driven software engineering) • Generate DDL for external model
  • 30. Use code generation wisely • Don’t loose language-level abstraction • Add your generator and data model to version control • Complete but new and therefor experimental in Slick
  • 32. Common use case for web apps Dynamically decide • displayed columns • filter conditions • sort columns / order
  • 33. Dynamic column class Coffees(tag: Tag) extends Table[CoffeeRow](…){ val name = column[String]("COF_NAME",…) } coffees.map(c => c.name) coffees.map(c => c.column[String]("COF_NAME") ) Be careful about security!
  • 35. sortDynamic 1 implicit class QueryExtensions3[E,T<: Table[E]] ( val query: Query[T,E] ){ def sortDynamic(sortString: String) : Query[T,E] = { // split string into useful pieces val sortKeys = sortString.split(',').toList.map( _.split('.').map(_.toUpperCase).toList ) sortDynamicImpl(sortKeys) } private def sortDynamicImpl(sortKeys: List[Seq[String]]) = ??? } suppliers.sortDynamic("street.desc,city.desc")
  • 36. sortDynamic 2 ... private def sortDynamicImpl(sortKeys: List[Seq[String]]) : Query[T,E] = { sortKeys match { case key :: tail => sortDynamicImpl( tail ).sortBy( table => key match { case name :: Nil => table.column[String](name).asc case name :: "ASC" :: Nil => table.column[String](name).asc case name :: "DESC" :: Nil => table.column[String](name).desc case o => throw new Exception("invalid sorting key: "+o) } ) case Nil => query } } } suppliers.sortDynamic("street.desc,city.desc")
  • 37. Summary • Query composition and re-use • Getting rid of boiler plate in Slick 2.0 – – – outer joins / auto joins auto increment code generation • Dynamic Slick queries

Editor's Notes

  1. Not an introductory talk, but with some Scala knowledge you should be able to follow even if you don’t know Slick &lt;number&gt;
  2. underlines -&gt; implicit conversions lazy query builder, explicit execution &lt;number&gt;
  3. &lt;number&gt;
  4. Embrace! Slick’s single coolest features! &lt;number&gt;
  5. C is element type alias, can be tuple or case class Table is row prototype &lt;number&gt;
  6. asColumn &lt;number&gt;
  7. C is element type of Coffees here, can be Coffee case class or Tuple &lt;number&gt;
  8. that’s the way to do association in slick, because it is lazy, composes val can put into method &lt;number&gt;
  9. string computed server side, which means still lazy and composable &lt;number&gt;
  10. none of what we have seen actually executed the query. just composed. call .run for ONE roundtrip &lt;number&gt;
  11. Run from shell or sbt as sourceGenerator or using separate &lt;number&gt;
  12. // not tied to a driver // entity class // table class // GetResult // Slick Hlists for &gt; 22 columns. &lt;number&gt;
  13. not big problem, when select exact &lt;number&gt;
  14. needs staged sbt build or manual execution &lt;number&gt;
  15. interfaces still useful &lt;number&gt;
  16. Limitations: - type is string - using db name Can use reflection instead &lt;number&gt;
  17. &lt;number&gt;
  18. https://github.com/cvogt/slick-presentation/tree/scala-exchange-2013 &lt;number&gt;