ScalaQuery1. ScalaQuery
flatMap(Oslo)
Trond Marius Øvstetun
© Mesan AS
2. Trond Marius Øvstetun
• Sjefskonsulentog
fagleder i Mesan
• Lidenskapelig
utvikler, arkitekt,
teamleder ++
• Java, Scala, HTML/js
• Alltid
på jakt etter
bedre, enklere
måter
© Mesan AS
3. Data
• Vi har behov for å lagre data
© Mesan AS
4. Data
• Vi har behov for å lagre data
• Ikke
alle bruker eller kan bruke
NoSQL
© Mesan AS
5. Data
• Vi har behov for å lagre data
• Ikke
alle bruker eller kan bruke
NoSQL
• konservativ it-avdeling?
• legacy – eksisterende data
• andre behov?
© Mesan AS
6. Data
• Vi har behov for å lagre data
• Ikke
alle bruker eller kan bruke
NoSQL
• konservativ it-avdeling?
• legacy – eksisterende data
• andre behov?
• av og til er relasjonsmodellen riktig
© Mesan AS
11. SQL
• Hvordan jobber vi med RDBMS på
best mulig måte?
• JDBC?
• ORM?
• Hibernate?
• Anorm? Mapper? Record?
© Mesan AS
15. En enkel domenemodell
«enumeration»
Artist
Genre
Album
name : String
Rock 1
biography : Text name : String
Pop *
Classic mainGenre : Genre release : Date
Blues founded : Date rating : Option[Int]
Rap split : Option[Date]
1
HipHop
Alternative *
*
* Song
name : String
Person duration : Int
firstname : String trackNumber : Int
lastname : String
biography : Text
© Mesan AS
16. Koble til en database
• Med javax.sql.DataSource
def myDs : DataSource = {...}
val db = Database.forDataSource(myDs)
© Mesan AS
18. Koble til en database
• Alternativt
Database.forName("jdbc/my_ds") //JNDI
Database.forURL("jdbc:h2:test")
© Mesan AS
19. Gjøre noe med databasen
val db = {...}
db withSession {
do_something()
}
import Database.threadLocalSession
© Mesan AS
21. Spørringer
val q = StaticQuery[Int] +
"select count(id) from Artists"
val count = q.first()
© Mesan AS
22. Spørringer
val q = StaticQuery[Int, (Int, String)] +
"select id, name from Artists where id = ?"
val (id,name) = q(1001).first
val artist : (Int, String) = q(1001).first
© Mesan AS
23. Mapping av resultat
case class Artist(id:Int, name:String)
val q = StaticQuery.query[Int, (Int, String)](
"select id, name from Artists where id = ?")
val qMapped = q.mapResult[Artist]({
case (a, b) => Artist(a, b)
})
var a:Artist = qMapped(1001).first()
© Mesan AS
24. Implisitt mapping av
case class Artist(id:Int, name:String)
implicit val artMapper = GetResult(
(r: PositionedResult) => new Artist(r<<, r<<))
val q = StaticQuery.query[Int, Art](
"select id, name from Artists where id = ?")
val a:Artist = q(1001).first
© Mesan AS
25. Insert / Update
import org.scalaquery.simple.StaticQuery._
val q = query[(String, String), Int](
"insert into Artists(name, biography) "+
"values (?, ?)")
val q2 = q("Seigmen", "")
q2.execute()
© Mesan AS
28. En enkel domenemodell
«enumeration»
Artist
Genre
Album
name : String
Rock 1
biography : Text name : String
Pop *
Classic mainGenre : Genre release : Date
Blues founded : Date rating : Option[Int]
Rap split : Option[Date]
1
HipHop
Alternative *
*
* Song
name : String
Person duration : Int
firstname : String trackNumber : Int
lastname : String
biography : Text
© Mesan AS
29. Mapping til en tabell
object Artists extends Table[
(Int, String, String,
Genre.Genre, Date, Option[Date])
]("ARTISTS") {
...
}
Artist
name : String
biography : Text
mainGenre : Genre
founded : Date
© Mesan AS
split : Option[Date]
30. Mapping til en tabell
object Artists extends Table (...) {
def id = column[Int]("ID", O PrimaryKey)
def name = column[String]("NAME")
def biography = column[String]("BIOGRAPHY")
def maingenre = column[Genre.Genre]("MAINGENRE")
def founded = column[Date]("FOUNDED")
def split = column[Option[Date]]("SPLIT")
} Artist
name : String
biography : Text
mainGenre : Genre
founded : Date
© Mesan AS
split : Option[Date]
31. Mapping til en tabell
object Artists extends Table (...) {
...
def * = id ~ name ~ biography ~
maingenre ~ founded ~ split
def i = name ~ biography ~
maingenre ~ founded ~ split
}
Artist
name : String
biography : Text
mainGenre : Genre
founded : Date
© Mesan AS
split : Option[Date]
32. Mapping med case-klasse
case class Artist(
id:Int,name:String, bio:String,
mainGenre:Genre.Genre, founded:Date,
split:Option[Date])
object Artists extends Table[Artist]("ARTISTS") {
def * = id ~ .. ~ split <> (Artist, Artist.unapply _)
} Artist
name : String
biography : Text
mainGenre : Genre
founded : Date
© Mesan AS
split : Option[Date]
33. Bruke egne typer
column[Genre.Genre]("MAINGENRE")
object Genre extends Enumeration {
type Genre = Value «enumeration»
Genre
val Rock = Value(1) Rock
Pop
Classic
...
Blues
Rap
HipHop
Alternative
val Alternative = Value(7)
}
implicit val genreMapper =
MappedTypeMapper.base[Genre.Genre, Int](
_.id, Genre(_))
© Mesan AS
34. Bruke egne typer
column[Duration]("DURATION")
case class Duration(mins:Int, secs:Int)
implicit object durationMapper extends
MappedTypeMapper[Duration, Int]
with BaseTypeMapper[Duration]
with NumericTypeMapper {
def map(dur: Duration) =
dur.mins * 60 + dur.secs
def comap(secs: Int) =
Duration(secs / 60, secs % 60)
} © Mesan AS
35. Inserts
val i: Int = Artists.i.insert(
("Seigmen", "", Genre.Rock,
date("1989-12-27"),
Some(date("2008-06-22"))))
// i is number of rows affected
val artists : List[(...)] = ...
val i = Artists.i.insertAll(artists :_*)
// i is Option[Int] - num affected
© Mesan AS
37. Enkle spørringer
def findArtist(id:Int) : Option[(...)] = {
val q = Artists.createFinderBy(_.id)
q(id).firstOption
}
def highRatedAlbums : List[...] = {
val q = Albums.filter(_.rating === (Six:Rating))
q.list
}
def unRatedAlbums : List[...] = {
val q = Albums.filter(_.rating isNull)
q.list
} © Mesan AS
40. Mer generelle spørringer
val q1 =
Artists.filter(_.maingenre === Genre.Rock)
val q = for {
a <- Artists if a.maingenre === Genre.Rock
} yield a
© Mesan AS
41. Generell spørring
val aQuery = for {
x1 <- generator
x2 <- generator
...
xN <- generator
guard
} yield projection
© Mesan AS
43. Parametrisert
val qArtist = for {
n <- Parameters[String]
a <- Artists if a.name === n
} yield a.id ~ a.name
val qTool = qArtist("Tool")
val (id, name) = qTool.first
© Mesan AS
44. Spørringer
• Er lazy
• Er immutable
• Dele og gjenbruke
• Byggeklosser
© Mesan AS
45. Spørringer
• Er lazy
• Er immutable
• Dele og gjenbruke
• Byggeklosser
val q1 = for (a <- Artists) yield a
val q2 = for (a <- q1) yield a.id ~ a.name
val q3 = q2.take(10).drop(50)
© Mesan AS
46. Update
val q = for {
a <- Artists if a.id === 1001
} yield a.name
q.update("updated") // num affected
© Mesan AS
47. Delete
val q = for {
a <- Albums if a.id === 1001
} yield a
q.delete
© Mesan AS
48. Relasjoner /
object Albums extends Table[(...)]("ALBUMS") {
def artist_id = column[Int]("ARTIST_ID")
def artist =
foreignKey("albums_artists_fk", artist_id, Artists)(_.id)
def * = id ~ name ~ release ~ rating ~ artist_id
}
Artist
Album
name : String
1
biography : Text name : String
*
mainGenre : Genre release : Date
founded : Date rating : Option[Int]
split : Option[Date]
© Mesan AS
49. Joins
Artist
val q = for { Album
name : String
1
biography : Text name : String
*
mainGenre : Genre release : Date
al <- Albums founded : Date
split : Option[Date]
rating : Option[Int]
a <- Artists
if al.artist_id === a.id
} yield a.name ~ al.name
val q = for {
al <- Albums
a <- al.artist
} yield a.name ~ al.name
val q = for {
(al, a) <- Albums innerJoin Artists on (_.artist_id is _.id)
} yield a.name ~ al.name
© Mesan AS
51. SLICK
• Generiskrammeverk for
spørringer mot data
• Ulike backends
• SQL,
NoSQL, json, WebServices,
XML ...
• Nærmere “vanlig” Scala-kode,
backend som scala Collection
© Mesan AS
54. takk for meg
tmo@mesan.no
@ovstetun
github.com/ovstetun/scala-persistence
© Mesan AS
Editor's Notes \n \n \n \n \n \n \n \n \n \n \n \n \n \n Herfra er det nesten bare kode - ROP ut om det er noe!!!\n Herfra er det nesten bare kode - ROP ut om det er noe!!!\n Herfra er det nesten bare kode - ROP ut om det er noe!!!\n &#x201C;min faste&#x201D; modell jeg bruker n&#xE5;r jeg tester nye systemer/teknologier etc\nhar laget ogs&#xE5; i mongo - fungerer str&#xE5;lende der ogs&#xE5;.\n \n \n \n settes typisk opp med cake/avhengigheter eller hvordan man &#xF8;nsker\nLIFT: i boot\n \n \n \n \n \n \n hva om jeg forteller ScalaQuery noe om databasen min?\n \n starte med &#xE5; definere strukturen p&#xE5; tabellen\n legge til kolonnenne som finnes\n definere projeksjoner p&#xE5; kolonnene som finnes\n* M&#xC5; definere STJERNE - denne brukes til og fra mapping som default\n kan gj&#xF8;res med caseklasser -> her er resten som tidligere\n<> kaller eg til-og-fra - og er laget for akkurat case-klasser\n ENUMS i scala, kan gj&#xF8;re tilsvarende med CASE OBJECTS\n mapping til/fra immutable verdiobjekter\n kaller insert p&#xE5; en projeksjon - med samme struktur som projeksjonen selv!\ngir kompileringsfeil ved feil struktur\ninsert er eager - gj&#xF8;res med en gang mot databasen\n \n vanligste operatorer -> trigger databasen!\n kan ogs&#xE5; skrives som...\n Generator == tabell (eller en fremmedn&#xF8;kkel)\n === mappes til = i basen\nlike\nupper, st&#xF8;rreEnn, mindreEnn etc - alt som finnes i databasen\n ingen av disse g&#xE5;r mot basen f&#xF8;r man faktisk kj&#xF8;rer en list/first\n sp&#xF8;rring representerer WHERE-delen\nyield-delen det som skal oppdateres - og inputstruktur til .update-kallet\n sp&#xF8;rring representerer WHERE-delen\n ForeignKeyQuery\n \n \n kommer for scala 2.10, bruker makroer\n \n \n