Patterns for Slick database applications Jan Christopher Vogt, EPFL Slick Team #scalax.
Slick: Bringing Scala’s Powerful Features to Your Database Access
-
Upload
rebecca-grenier -
Category
Technology
-
view
1.126 -
download
2
Transcript of Slick: Bringing Scala’s Powerful Features to Your Database Access
![Page 2: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/2.jpg)
Intro to Slick
Static Typing + Compilation = Type Safety
For-Comprehensions
Compositionality: build complex queries out of simple
parts
@beckythebest
![Page 3: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/3.jpg)
Introduction to Slick
@beckythebest
![Page 4: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/4.jpg)
Slick Connection Drivers
Oracle ($$$)
DB2 ($$$)
SQL Server ($$$)
@beckythebest
Note: Connection Pooling is your
responsibility
PostgreSQL
MySQL
Access
Derby
H2
Hsqldb
SQLite
![Page 5: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/5.jpg)
The Files Table
@beckythebest
Field Type Null
id int (10) unsigned NO
uid int (10) unsigned NO
path varchar (255) NO
filetype varchar (255) NO
![Page 6: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/6.jpg)
Table Definitions:
Mapping to tuples
@beckythebest
![Page 7: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/7.jpg)
Table Definitions:
Mapping to case classes
@beckythebest
![Page 8: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/8.jpg)
How the 22-item
tuple Limit Affects SlickWhat if your table has more than 22 columns?
Define multiple Table Classes that refer to the same table –
similar to “views”
* There is a workaround for Scala >= 2.10.3 where you can
use HList instead
@beckythebest
![Page 9: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/9.jpg)
Queries in Slick
@beckythebest
Every query starts out as a TableQuery first:
val files = TableQuery[Files]
is the equivalent of
select * from files;
(You can use .selectStatement on any query to see
the SQL for a select statment that is generated
behind the scenes)
![Page 10: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/10.jpg)
A Quick Full Example
val allFiles = db withSession {
implicit session =>
files.run
}
allFiles is a Vector of File case class objects
files is just the query and remains so until it is invoked
(with .run)
@beckythebest
![Page 11: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/11.jpg)
Building Your Query:
Adding a Where Clause
@beckythebest
With .filter
files.filter(_.filetype === ‘pdf’)
In SQL: select * from files where filetype= ’pdf’
• In Slick, equals is ===
• Not-Equals is =!=
![Page 12: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/12.jpg)
Use Method Chaining to Add
More Clauses to Your Query
Slick: files.filter(_.filetype === “pdf”).filter(_.id < 20000)
SQL: select * from files where filetype= “pdf” and id < 20000
@beckythebest
Reminder: You are not really filtering
yet;
you are building a Query.
![Page 13: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/13.jpg)
Query Building
with Modifiers from Slick’s DSL
take
files.take(5) SQL: select * from files limit 5
Dropfiles.drop(5) SQL: select * from files offset 5
length
files.length SQL: select count(*) from files
map
flatMap
sortBy SQL: sort by
@beckythebest
![Page 14: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/14.jpg)
Connecting to Your Database
in Slick
@beckythebest
Connect with Database.forURL
There is also forDriver, forName, and forDataSource
![Page 15: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/15.jpg)
Queries Need Sessions
Use that Database Connection to get a
Session
@beckythebest
This is a static
session
![Page 16: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/16.jpg)
Just Say No to
@beckythebest
AKA A non-explicit session you hope was opened earlier this
thread
You can’t count on your thread having a session in an
asyncronous world
Dynamic
Sessions
Dynamic Sessions
![Page 17: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/17.jpg)
Query Invokers
Vector of results
List of results
First result or Exception
Some(first) or None
Nothing
@beckythebest
files.run
files.list
files.first
files.firstOption
files.execute
Invoker Returns
![Page 18: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/18.jpg)
Invoke a Delete Query
scala> files.deleteStatement
res5: String = delete from `files`
invoke with .delete
files.delete
@beckythebest
![Page 19: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/19.jpg)
Just Delete One Record
Reduce your query with filter:
> files.filter(_.id === 56).deleteStatement
res6: String = delete from `files` where `files`.`id` = 56
Invoked:
files.filter(_.id === 56).delete
@beckythebest
![Page 20: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/20.jpg)
Insert Query Invoker
scala> files.insertStatement
res1: String = insert into `files` (`id`,`path`,`filetype`,`uid`) values (?,?,?,?)
invoke with +=
files += File(0, “path to file”, “type”, 333)
@beckythebest
![Page 21: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/21.jpg)
Update Query Invoker
scala> files.map(_.path).updateStatement
res4: String = update `files` set `path` = ?
invoke with .update()
files.map(_.path).update(“new path to file”)
@beckythebest
![Page 22: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/22.jpg)
Best Practice: Build your Query
Completely BEFORE Invoking
The commands below look similar but have very
different performance:
files.take(5).run
SQL: (select * from files limit 5)
files.run.take(5)
SQL: (select * from files) take 5
@beckythebest
![Page 23: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/23.jpg)
What good is all this
Typing?
@beckythebest
No error?
No big deal!
Use SQL to query the files table by fid (which is an integer)
What happens when a non-integer value gets passed in?
![Page 24: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/24.jpg)
VS. Static Typing
@beckythebest
We get the following error at compile time:
If you do the same thing in Slick:
![Page 25: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/25.jpg)
Joining: Introducing The Users
Table
@beckythebest
![Page 26: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/26.jpg)
Files has a new column: uid
@beckythebest
![Page 27: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/27.jpg)
Joining with For-
Comprehensions
SQL: select * from users,files where files.uid = users.id
@beckythebest
When invoked (with innerJoinFileUser.run) this
returns a Collection of tuples with the first item being of
type User and the second being of type File
![Page 28: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/28.jpg)
Where Clauses in For-
Comprehensions
Use filter expressions instead of filters
Example: limit to only files owned by Sarah:
@beckythebest
![Page 29: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/29.jpg)
Slick also has its own Join
Methods
We just looked at this query joined with a for-
comprehension:
@beckythebest
Same query joined with innerJoin method:
![Page 30: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/30.jpg)
The SQL Behind the
Scenes
Joined with a for-comprehension
select x2.`uid`, x2.`name`, x2.`mail`, x2.`status`, x3.`id`, x3.`path`, x3.`filetype`, x3.`uid` from `users` x2, `files` x3 where x3.`id` = x2.`uid`
Joined with the innerJoin method
select x2.x3, x2.x4, x2.x5, x2.x6, x7.x8, x7.x9, x7.x10, x7.x11 from (select x12.`id` as x3, x12.`name` as x4, x12.`mail` as x5, x12.`status` as x6 from `users` x12) x2 inner join (select x13.`id` as x8, x13.`path` as x9, x13.`filetype` as x10, x13.`uid` as x11 from `files` x13) x7 on x2.x3 = x7.x11
@beckythebest
![Page 31: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/31.jpg)
Return Anything You Want
@beckythebest
With these for-comprehension use yield to reduce the data
you want returned
Instead of yield(u, f) you could have
yield(f)
yield (u.name, f.path)
yield (f.path)
![Page 32: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/32.jpg)
Slick Outer Joins
You can’t do these with for-comprehensions
f.path.? turns values into Options (there might not be
files for every user)
f.? doesn’t work
@beckythebest
* Remember, you can always use plain SQL with Slick
![Page 33: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/33.jpg)
Query Compositionality
@beckythebest
Every query does Double
Duty:
1. Invoke to get data
2. Use as a building block for
another query
![Page 34: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/34.jpg)
Create Query Methods
object Files {
def byType(filetype: String) = files.filter(_.filetype === filetype)
}
implicit session =>
Files.byType(“text/html”).list
Returns a list of HTML File case classes
@beckythebest
![Page 35: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/35.jpg)
Let’s Look at the SQL Behind
That
The method itself is not a Query, but it returns a Query
scala> filesByType.selectStatement
ERROR
scala> filesByType(“pdf").selectStatement
res3: String = select * from `files` x2 where x2.`filetype` = ‘pdf'
@beckythebest
![Page 36: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/36.jpg)
Composing Queries out of
Queries
object Users {
def userPDFS(email: String) = for {
u <- users if u.email === email
f <- Files.byType(“pdf”) if f.uid === u.id
} yield (f)
}
@beckythebest
![Page 37: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/37.jpg)
Quick Look at the SQL
scala> userPDFS("[email protected]").selectStatement
res0: String = select files`id`, files.`path`, files.`filetype`, files.`id` from `users`, `files` where ((users.`mail` = '[email protected]') and (files.`filetype` = 'application/pdf')) and (files.`uid` = users.`id`)
@beckythebest
* There are many more advanced ways
![Page 38: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/38.jpg)
Use the Combos in Code
Now to get all Sarah’s PDFS is a short, clear statement:
val sarahsPdfs = db withSession {
implicit session =>
Users.userPDFS(“[email protected]”).list
}
sarahsPDFS is now a List of File case classes OR
continue to build on it further
@beckythebest
![Page 39: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/39.jpg)
Slick Drawbacks
Not a lot of documentation for advanced use
Re-using Slicks collection methods for query building can
be confusing
Learning curve (compared to already knowing SQL) (If
you do)
Not the most efficient SQL
@beckythebest
![Page 40: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/40.jpg)
Save Yourselves!
There is now code generation in Slick!
You don’t have to write out 65 Table class definitions like I did
@beckythebest
![Page 41: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/41.jpg)
Summary
Slick uses Scala’s best features to bring type safety and
composability to your Relational Database access
• Static Typing
• Collection Methods
• For-Comprehensions
• Compositionality
@beckythebest
![Page 42: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/42.jpg)
Resources
Slick Documentation: http://slick.typesafe.com/doc/2.1.0/
Activator’s Hello Slick! http://typesafe.com/activator/template/hello-slick
Adam Mackler’s Learning Slick V2 https://mackler.org/LearningSlick2/
IRC chat room #scala on Freenode
Advanced Query Composing: http://slick.typesafe.com/talks/2013-12-03_Scala-eXchange/2013-12-03_Patterns-for-Slick-database-applications-Scala-eXchange.pdf
Working around the 22 tuple limit: http://stackoverflow.com/questions/20555304/how-can-i-use-the-new-slick-2-0-hlist-to-overcome-22-column-limit
@beckythebest
![Page 43: Slick: Bringing Scala’s Powerful Features to Your Database Access](https://reader034.fdocuments.us/reader034/viewer/2022042817/55a20a7f1a28ab9b368b46a6/html5/thumbnails/43.jpg)
Thank You
Questions?
Rebecca Grenier
@beckythebest
Special Thanks to: Underscore Consulting
@beckythebest