This talk will teach you how to use Slick in practice, based on our experience at EatingWell Media Group. Slick is a totally different (and better!) relational database mapping tool that brings Scala’s powerful features to your database interactions, namely: static-checking, compile-time safety, and compositionality.
Here at EatingWell, we have learned quite a bit about Slick over the past two years as we transitioned from a PHP website to Scala. I will share with you tips and tricks we have learned, as well as everything you need to get started using Slick in your Scala application.
I will begin with Slick fundamentals: how to get started making your connection, the types of databases it can access, how to actually create table objects and make queries to and from them. We will using these fundamentals to demonstrate the powerful features inherited from the Scala language itself: static-checking, compile-time safety, and compositionality. And throughout I will share plenty of tips that will help you in everything from getting started to connection pooling options and configuration for use at scale.
2. Intro to Slick
Static Typing + Compilation = Type Safety
For-Comprehensions
Compositionality: build complex queries out of simple
parts
@beckythebest
8. How the 22-item
tuple Limit Affects Slick
What 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
9. Queries in Slick
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)
@beckythebest
10. 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
11. Building Your Query:
Adding a Where Clause
With .filter
files.filter(_.filetype === ‘pdf’)
In SQL: select * from files where
filetype= ’pdf’
• In Slick, equals is ===
• Not-Equals is =!=
@beckythebest
12. 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
Reminder: You are not really filtering
yet;
@beckythebest
you are building a Query.
13. Query Building
with Modifiers from Slick’s DSL
take
files.take(5) SQL: select * from files limit 5
Drop
files.drop(5) SQL: select * from files offset 5
length
files.length SQL: select count(*) from files
map
flatMap
sortBy SQL: sort by
@beckythebest
14. Connecting to Your Database
in Slick
Connect with Database.forURL
There is also forDriver, forName, and forDataSource
@beckythebest
15. Queries Need Sessions
Use that Database Connection to get a
Session
@beckythebest
This is a static
session
16. Just Say No to
Dynamic Sessions
AKA A non-explicit session you hope was opened earlier this
thread
Dynamic
Sessions
You can’t count on your thread having a session in an
@beckythebest
asyncronous world
17. Query Invokers
Invoker Returns
Vector of results
List of results
First result or Exception
Some(first) or None
Nothing
files.run
files.list
files.first
files.firstOption
files.execute
@beckythebest
18. Invoke a Delete Query
scala> files.deleteStatement
res5: String = delete from `files`
invoke with .delete
files.delete
@beckythebest
19. 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
20. 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
21. 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
22. 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
23. What good is all this
Typing?
Use SQL to query the files table by fid (which is an integer)
What happens when a non-integer value gets passed in?
@beckythebest
No error?
No big deal!
24. VS. Static Typing
If you do the same thing in Slick:
We get the following error at compile time:
@beckythebest
27. Joining with For-
Comprehensions
SQL: select * from
users,files where
files.uid = users.id
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
@beckythebest
28. Where Clauses in For-
Comprehensions
Use filter expressions instead of filters
Example: limit to only files owned by Sarah:
@beckythebest
29. Slick also has its own Join
Methods
We just looked at this query joined with a for-comprehension:
Same query joined with innerJoin method:
@beckythebest
30. 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
31. Return Anything You Want
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)
@beckythebest
32. 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
* Remember, you can always use plain SQL with Slick
@beckythebest
33. Query Compositionality
Every query does Double
Duty:
1. Invoke to get data
2. Use as a building block for
another query
@beckythebest
34. 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
35. 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
36. 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
37. Quick Look at the SQL
scala>
userPDFS("sarah@eatingwell.com").selectStatement
res0: String = select files`id`, files.`path`,
files.`filetype`, files.`id` from `users`,
`files` where ((users.`mail` =
'sarah@eatingwell.com') and (files.`filetype` =
'application/pdf')) and (files.`uid` =
users.`id`)
* There are many more advanced ways
@beckythebest
38. 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(“sarah@eatingwell.com”).list
}
sarahsPDFS is now a List of File case classes OR
continue to build on it further
@beckythebest
39. 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
40. Save Yourselves!
There is now code generation in Slick!
You don’t have to write out 65 Table class definitions like I did
@beckythebest
41. 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
42. 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
43. Thank You
Questions?
Rebecca Grenier
rebeccagrenier@gmail.com
@beckythebest
Special Thanks to: Underscore Consulting
@beckythebest
Editor's Notes
Should I go over relational databases vs. whatever mongo is