slick: bringing scala’s powerful features to your database access
TRANSCRIPT
Intro to Slick
Static Typing + Compilation = Type Safety
For-Comprehensions
Compositionality: build complex queries out of simple
parts
@beckythebest
Introduction to Slick
@beckythebest
Slick Connection Drivers
Oracle ($$$)
DB2 ($$$)
SQL Server ($$$)
@beckythebest
Note: Connection Pooling is your
responsibility
PostgreSQL
MySQL
Access
Derby
H2
Hsqldb
SQLite
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
Table Definitions:
Mapping to tuples
@beckythebest
Table Definitions:
Mapping to case classes
@beckythebest
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
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)
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
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 =!=
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.
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
Connecting to Your Database
in Slick
@beckythebest
Connect with Database.forURL
There is also forDriver, forName, and forDataSource
Queries Need Sessions
Use that Database Connection to get a
Session
@beckythebest
This is a static
session
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
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
Invoke a Delete Query
scala> files.deleteStatement
res5: String = delete from `files`
invoke with .delete
files.delete
@beckythebest
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
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
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
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
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?
VS. Static Typing
@beckythebest
We get the following error at compile time:
If you do the same thing in Slick:
Joining: Introducing The Users
Table
@beckythebest
Files has a new column: uid
@beckythebest
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
Where Clauses in For-
Comprehensions
Use filter expressions instead of filters
Example: limit to only files owned by Sarah:
@beckythebest
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:
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
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)
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
Query Compositionality
@beckythebest
Every query does Double
Duty:
1. Invoke to get data
2. Use as a building block for
another query
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
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
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
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
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
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
Save Yourselves!
There is now code generation in Slick!
You don’t have to write out 65 Table class definitions like I did
@beckythebest
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
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
Thank You
Questions?
Rebecca Grenier
@beckythebest
Special Thanks to: Underscore Consulting
@beckythebest