3. What is Slick
for{e ← Employees} yield e
“select * from employee”
●
Slick is a library written in scala for taking to database from scala programs.
●
It is currently based on Jdbc.
●
It is Successor of ScalaQuery.
●
Developed By Typesafe and EPFL(École Polytechnique Fédérale de Lausanne)
4. Slick's API
●
Lifted embedding
- The lifted embedding is Slick's stable query API which is based on ScalaQuery.
●
Direct embedding
- The direct embedding is a new experimental API for Slick that uses macros to
allow expressions operating on standard Scala types to be used for database
queries
●
Plain SQL
- Slick also allows you to write your own SQL queries and execute them with an
API which is optimized for Scala, much easier to use and more concise than
JDBC.
7. Easy
●
Access stored data just like Scala collections.
- for{ e ← Emp if( p.age === 25 )} yield e
●
Unified session management based on JDBC Connections
-forURL(url: String, user: String = null, password: String = null, prop:
Properties = null, driver: String = null): Database
- database.withSession{ // put code here }
●
Supports SQL if you need it
●
Simple setup
8. Concise
●
Slick uses scala syntax.
●
Fetch results without pain
- [jdbc] val sql = "select * from employee where name = ?“
val st = conn.prepareStatement( sql )
try {
st.setString(1, name)
val rs = st.executeQuery()
val b = new ListBuffer[(Int, String)]
while(rs.next)
b.append((rs.getInt(1), rs.getString(2)))
b.toList
} finally st.close()
- [slick]( for( e <- Epmloyees if e.name === name ) yield e).list
9. Safe
●
No SQL-injections
●
Compile-time safety (types, names, no typos, etc.)
-[jdbc] "select
* from employee wehres nami = ' " + name + " ' "
-[slick]for( e <- Employees if e.name === name ) yield e
●
Type-safe use of stored procedures
val getfeedbackByQuestionId =
SimpleFunction.unary[ Int,String] ("getfeedbackbyquestionid")
10. Composable
Projects
Employees
*
*
EmpProjects
●
def employeeByJoiningDate( from:Date, to:Date ) = Employees.filter(
emp => emp.joiningDate >= from && emp.joiningDate <= to )
// projects with employee name (joining between 1-1-2013 and 1-10-2013)
for{ emp <- employeeByJoiningDate(1-1-2013, 1-10-2013)
empproject <- EmpProjects if (emp.id === empproject.empId)
project <- Projects if (empproject.projectId === project.id)
} yield (emp.name, project.name, project.location)
11. Explicit
●
No lazy loading means predictable performance.
●
Only read the data you need.
●
State less
- no caches
12. Overview
Accessing databases using Slick’s lifted embedding requires the following
steps: 1. Add the dependencies
2. Pick a driver for a particular database
3. Database Connection
- Database object
- Session
5. Describe Database schema
6. Write queries
* In this presentation we are using postgres database.
13. Dependencies
Add dependencies in your build.sbt
libraryDependencies ++= List(
"com.typesafe.slick" %% "slick" % "1.0.1",
"org.slf4j" % "slf4j-nop" % "1.6.4",
"postgresql" % "postgresql" % "9.1-901.jdbc4"
)
- Slick uses SLF4J for its own debug logging so you also need to add an SLF4J
implementation. Here we are using slf4j-nop to disable logging.
- You have to replace this with a real logging framework like Logback if you want to see
log output
14. Database drive
Import database drive :
// for postgres database
●
import scala.slick.driver.PostgresDriver.simple._
- Since we are using postgres as our database system, we need to import features from
Slick’s PostgresDriver. A driver’s simple object contains all commonly needed imports
from the driver and other parts of Slick such as session handling.
15. Database object
Factory methods for creating Database objects:
●
def forDataSource(ds: DataSource): Database
- Create a Database based on a DataSource.
●
def forName(name: String): Database
- Create a Database based on the JNDI name of a DataSource.
●
def forDriver(driver: Driver, url: String, user: String = null, password: String = null,
prop: Properties = null): Database
- Create a Database that directly uses a Driver to open new connections. This is needed to open a
JDBC URL with a driver that was not loaded by the system ClassLoader.
●
def forURL(url: String, user: String = null, password: String = null, prop: Properties
= null, driver: String = null): Database
- Create a Database that uses the DriverManager to open new connections.
16. Session
A database instance to which connections can be created. Encapsulates either a DataSource
or parameters for DriverManager.getConnection().
Methods for session creation:
def createSession(): Session
- Create a new session. The session needs to be closed explicitly by calling its close()
method.
def withSession[T](f: (Session) ⇒ T): T
- Run the supplied function with a new session and automatically close the session at
the end.
def withTransaction[T](f: (Session) ⇒ T): T
- Run the supplied function with a new session in a transaction and automatically close the
session at the end.
17. Example
Get database object:
val dbObject = Database.forURL("jdbc:postgresql://localhost:5432/slickdemo", "sky",
"satendra", null, "org.postgresql.Driver")
Get Session:
dbObject.withSession{ implicit session: Session =>
// write query here
}
* End of block session closed automatically.
18. Database schema structure
Employees
Projects
id integer (primary key)
name varchar(100)
email varchar(100)
designation varchar(100)
doj date
id integer (primary key)
name varchar(100)
location varchar(100)
EmpProjects
empid integer(FKey)
projectid integer(FKey)
primarykey(empid,projectid)
19. Database schema in slick
For employee table:
case class Emp(id: Int, name: String, email: String, designation: String, doj: Date)
object Employees extends Table[Emp]("emp") {
def id = column[Int]("id", O.PrimaryKey)
def name = column[String]("name", O.NotNull, O.DBType("VARCHAR(100)"))
def email = column[String]("email", O.NotNull, O.DBType("VARCHAR(100)"))
def designation = column[String]("designation", O.NotNull, O DBType ("VARCHAR(100)"))
def doj = column[Date]("doj", O.NotNull)
def * = id ~ name ~ email ~ designation ~ doj <> (Emp.apply _, Emp unapply _)
}
20. Database schema in slick
For project table:
case class Project(id: Int, name: String, location: String)
object Projects extends Table[Project]("project") {
def id = column[Int]("id", O.PrimaryKey, O.DBType("INT"))
def name = column[String]("name", O.DBType("VARCHAR(100)"))
def location = column[String]("location", O.DBType("VARCHAR(100)"))
def * = id ~ name ~ location <> (Project, Project unapply _)
}
22. Supported datatype
●
Numeric types: Byte, Short, Int, Long, BigDecimal, Float, Double
●
LOB types: java.sql.Blob, java.sql.Clob, Array[Byte]
●
Date types: java.sql.Date, java.sql.Time, java.sql.Timestamp
●
Boolean
●
String
●
Unit
●
java.util.UUID
If you need a custom column type you can implement TypeMapper and TypeMapperDelegate. The most
common scenario is mapping an application-specific type to an already supported type in the database.
This can be done much simpler by using a MappedTypeMapper which takes care of all the boilerplate:
def arrayTypeMapped = MappedTypeMapper.base[Array[Int], String](
array => array mkString ",",
str => { if (str != "") { (str split "," map Integer.parseInt) } else { Array() } })
24. Data Definition Language
Create tables:
dbObject withSession { implicit session: Session =>
val ddl= Employees.ddl ++ Projects.ddl ++ EmpProjects.ddl
ddl.create
}
Drop tables:
dbObject withSession { implicit session: Session =>
val ddl= Employees.ddl ++ Projects.ddl ++ EmpProjects.ddl
ddl.drop
}
25. Insert
●
Insert a row in Employees table:
Employees.insert( Emp(8, "satendra kumar", "satendra@knoldus.com", "consultant", java.sql.Date.valueOf("201306-3")) ) )
●
Insert List of rows in Employees table:
val listOfEmp = List(
Emp(1, "Janmejani", "Janmejani@knoldus.com", "consultant", java.sql.Date.valueOf("2012-11-26")),
Emp(2, "Anand", "anand@knoldus.com", "consultant", java.sql.Date.valueOf("2013-07-01")),
Emp(3, "Rishi Khandelwal ", "rishi@knoldus.com", "consultant", java.sql.Date.valueOf("2012-08-29"))
)
Employees.insertAll( listOfEmp: _*)
26. Retrieve row
●
Retrieve all rows:
Query(Employees).list
or
(for(emp <-Employees)yield(emp)).list
●
Retrieve all rows with only two columns(name and joning date):
( for(emp <-Employees) yield (emp.name, emp.doj) ).list
or
(Employees map {emp =>( emp.name,emp.doj)}).list
27. Update/Delete
●
Update name of employee (where employee id is 8):
val query = for (emp <- Employees if emp.id === 8) yield (emp.name)
query.update("satendra")
●
Delete employee (where employee id is 8 ):
val query = for (emp <- Employees if emp.id === 8) yield (emp)
query.delete
●
Delete all employee:
val query = for (emp <- Employees) yield (emp)
query.delete
28. Sorting and Filtering
●
Sort employee list by id:
(for (emp <- Employees) yield (emp)).sortBy(emp => emp.id).list
reverse sorting
(for (emp <- Employees) yield (emp)).sortBy(emp => emp.id.desc).list
●
Sort employee list by joining date:
(for (emp <- Employees) yield (emp)).sortBy(emp => emp.id).list
●
Filter employees which have joining date between 2013-07-10 and 2012-11-2:
Employees.filter{emp => (emp.doj <= Date.valueOf("2013-07-10") &&
Date.valueOf("2012-11-26")) }.list
emp.doj >=
29. Joins
●
Employee with project name and project location:
(for {
emp <- Employees
empproject <- EmpProjects if emp.id === empproject.empId
project <- Projects if (empproject.projectId === project.id)
} yield (emp.name, project.name,project.location)).list
30. Union
val query1 = Employees.filter { emp => emp.id === 8 }
val query2 = Employees.filter { emp => emp.id ===2}
(query1 union query2).list