Slick: Bringing Scala’s Powerful Features to Your Database Access

Post on 12-Jul-2015

1126 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

Transcript

The Slick Library

Rebecca Grenier

rebeccagrenier@gmail.com

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("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`)

@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(“sarah@eatingwell.com”).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

rebeccagrenier@gmail.com

@beckythebest

Special Thanks to: Underscore Consulting

@beckythebest

top related