Slick select row by id - scala

Selecting a single row by id should be a simple thing to do, yet I'm having a bit of trouble figuring out how to map this to my object.
I found this question which is looking for the same thing but the answer given does not work for me.
Currently I have this that is working, but it doesn't seem as elegant as it should be.
def getSingle(id: Long):Option[Category] = withSession{implicit session =>
(for{cat <- Category if cat.id === id} yield cat ).list.headOption
//remove the .list.headOption and the function will return a WrappingQuery
}
I feel getting a list then taking headOption is just bulky and unnecessary. I must be missing something.
If it helps, here is more of my Category code
case class Category(
id: Long = 0L,
name: String
)
object Category extends Table[Category]("categories"){
def name = column[String]("name", O.NotNull)
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def * = id ~ name <> (Category.apply _, Category.unapply _)
...
}
Is there an easier way to just get an Option[T] from an ID using Slick?
Solution There was a driver issue. I couldn't use .firstOption but upgraded to mysql jdbc 5.1.25 and all is well!

You can do this:
def getSingle(id: Long):Option[Category] = withSession{implicit session =>
Query(Category).where(_.id === id).firstOption
}
If you use this query quite often then you should consider QueryTemplate:
val byId = t.createFinderBy( t => t.id )
This will create a precompiled prepared statement that you can use from your method
def getSingle(id: Long):Option[Category] = byId(id).firstOption

Firstly, you may try is to use desugared version of the same code:
Category.filter{ _.id === id }.list.headOption
It looks much cleaner.
Also you may use firstOption method:
Category.filter{ _.id === id }.firstOption

I am using slick 1.0.1 with Play 2.2.1 and the following works for me.
val byId = createFinderBy(_.id)
Then call it from a method.
def findById(id: Int): Option[Category] = DB.withSession { implicit session =>
Category.byId(id).firstOption
}
Please note, the DB.withSession is a method from the play framework.
If you are not using Play, the method would be something like below.
def findById(id: Int)(implicit session: Session): Option[Category] = {
Category.byId(id).firstOption
}

Related

Generic update with mapping in Slick

I'm writing a CRUD app using Slick, and I want my update queries to only update a specific set of columns and I use .map().update() for that.
I have a function that returns a tuple of fields that can be updated in my table definition (def writableFields). And I have a funciton that returns a tuple of values to write there extracted from a case class.
It works fine, but it's annoying to create a repo and write the whole update function for every table. I want to create a generic form of this function, and make my table and it's companion object to extend some trait. But I cannot come up with correct type definitions.
Slick expects output of map() to be somehow compatible with the output of update. And I don't know how to make a generic type for tuples.
Is it even possible to accomplish? Or is there an alternative way to limit code duplication? Ideally I want to avoid writing Repos at all and just either instantiate a generic class or call a generic method.
object ProjectsRepo extends BaseRepository[Projects, Project] {
protected val query = lifted.TableQuery[Projects]
def update(id: Long, c: Project): Future[Option[Project]] = {
val q = filterByIdQuery(id).map(_.writableFields)
.update(Projects.mapFormToTable(c))
(db run q).flatMap(
affected =>
if (affected > 0) {
findOneById(id)
} else {
Future(None)
}
)
}
}
class Projects(tag: Tag) extends Table[Project](tag, "projects") with IdentifiableTable[Long] {
val id = column[Long]("id", O.PrimaryKey, O.AutoInc)
val title = column[String]("title")
val slug = column[String]("slug")
val created_at = column[Timestamp]("created_at")
val updated_at = column[Timestamp]("updated_at")
def writableFields =
(
title,
slug
)
def readableFields =
(
id,
created_at,
updated_at
)
def allFields = writableFields ++ readableFields // shapeless
def * = allFields <> (Projects.mapFromTable, (_: Project) => None)
}
object Projects {
def mapFormToTable(c: Project): FormFields =
(
c.title,
c.slug
)
}

Solution in Slick to Map a Future[Option] DB result to a value

I'm doing this in play framework to get the relation between a command and a execution table using slick.
class ExecutionsTable(tag: Tag) extends BaseTable[Execution](tag, "executions") {
def name = column[String]("name")
def execution = column[String]("execution")
def commandId = column[Long]("command_id")
def * = (id, name, execution, commandId) <> (Execution.tupled, Execution.unapply)
def command = foreignKey("commands", commandId, commandsTableQ)(_.id)
implicit val CommandToLong = MappedColumnType.base[Long, Command] (
id => {
val cmd = Await.result(new CommandsDAO().findById(id), 1 second)
cmd.get
},
cmd => cmd.id
)
findById returns a Future[Option[Command]] I'm trying to use this method which I'm not sure is the best solution for this.
What would you recommend?
Do you want to handle errors? If you do, I suggest you use a flatMap and then pattern match again to see if you function actually returns something. Here you go, Something along these lines :
val x : Future[Option[String]] = Future(Option("1"))
val id = x.flatMap(optId => optId match {
case Some(id) => id
case None => throw new Exception("No ID.")
}

How to return full row using Slick's insertOrUpdate

I am currently learning Play2, Scala and Slick 3.1, and am pretty stuck with the syntax for using insertOrUpdate and wonder if anyone can please help me.
What I want to do is to return the full row when using insertOrUpdate including the auto inc primary key, but I have only managed to return the number of updated/inserted rows.
Here is my table definition:
package models
final case class Report(session_id: Option[Long], session_name: String, tester_name: String, date: String, jira_ref: String,
duration: String, environment: String, notes: Option[String])
trait ReportDBTableDefinitions {
import slick.driver.PostgresDriver.api._
class Reports(tag: Tag) extends Table[Report](tag, "REPORTS") {
def session_id = column[Long]("SESSION_ID", O.PrimaryKey, O.AutoInc)
def session_name = column[String]("SESSION_NAME")
def tester_name = column[String]("TESTER_NAME")
def date = column[String]("DATE")
def jira_ref = column[String]("JIRA_REF")
def duration = column[String]("DURATION")
def environment = column[String]("ENVIRONMENT")
def notes = column[Option[String]]("NOTES")
def * = (session_id.?, session_name, tester_name, date, jira_ref, duration, environment, notes) <> (Report.tupled, Report.unapply)
}
lazy val reportsTable = TableQuery[Reports]
}
Here is the section of my DAO that relates to insertOrUpdate, and it works just fine, but only returns the number of updated/inserted rows:
package models
import com.google.inject.Inject
import play.api.db.slick.DatabaseConfigProvider
import scala.concurrent.Future
class ReportsDAO #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends DAOSlick {
import driver.api._
def save_report(report: Report): Future[Int] = {
dbConfig.db.run(reportsTable.insertOrUpdate(report).transactionally)
}
}
I have tried playing with "returning" but I can't get the syntax I need and keep getting type mismatches e.g. the below doesn't compile (because it's probably completely wrong!)
def save_report(report: Report): Future[Report] = {
dbConfig.db.run(reportsTable.returning(reportsTable).insertOrUpdate(report))
}
Any help appreciated - I'm new to Scala and Slick so apologies if I'm missing something really obvious.
Solved - posting it incase it helps anyone else trying to do something similar:
//will return the new session_id on insert, and None on update
def save_report(report: Report): Future[Option[Long]] = {
val insertQuery = (reportsTable returning reportsTable.map(_.session_id)).insertOrUpdate(report)
dbConfig.db.run(insertQuery)
}
Works well - insertOrUpdate doesn't returning anything it seems on update, so if I need to get the updated data after the update operation I can then run a subsequent query to get the information using the session id.
You cannot return whole Report, first return Id (returning(reportsTable.map(_.session_id))) and then get whole object
Check if report exists in the database if it exists update it, if not go ahead inserting the report into the database.
Note do above operations in all or none fashion by using Transactions
def getReportDBIO(id: Long): DBIO[Report] = reportsTable.filter(_.session_id === id).result.head
def save_report(report: Report): Future[Report] = {
val query = reportsTable.filter(_.session_id === report.session_id)
val existsAction = query.exists.result
val insertOrUpdateAction =
(for {
exists <- existsAction
result <- exists match {
case true =>
query.update(report).flatMap {_ => getReportDBIO(report.session_id)}.transactionally
case false => {
val insertAction = reportsTable.returning(reportsTable.map(_.session_id)) += report
val finalAction = insertAction.flatMap( id => getReportDBIO(id)).transactionally //transactionally is important
finalAction
}
}
} yield result).transactionally
dbConfig.db.run(insertOrUpdateAction)
}
Update your insertOrUpdate function accordingly
You can return the full row, but it is an Option, as the documentation states, it will be empty on an update and will be a Some(...) representing the inserted row on an insert.
So the correct code would be
def save_report(report: Report): Future[Option[Report]] = {dbConfig.db.run(reportsTable.returning(reportsTable).insertOrUpdate(report))}

(Not so) advanced mapped projection in Slick 3.1

I'm doing R&D whether we should or should not use Scala 2.11/Play 2.4/Slick 3.1 stack for out new app. I did some Scala programming few years ago and kept it as my favourite language for small personal projects but advanced concepts still are a mystery to me.
After reading this blog post by Matt Handler I want to replicate this behaviour in my PoC app but I run into some problems.
case class BaseModel[A] (id: Long, model: A)
object BaseModel {
import scala.language.implicitConversions
implicit def toModel[A](modelWithId: BaseModel[A]): A = modelWithId.model
}
case class Ingredient(name: String, description: String)
class IngredientsTable(tag: Tag) extends Table[BaseModel[Ingredient]](tag, "ingredients") {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def description = column[String]("description")
def * = (id, name, description) <> ??? // ((BaseModel[Ingredient].apply _).tupled, BaseModel[Ingredient].unapply)
}
My question is what should I place instead of ??? as commented out pard doesn't work for obvious reason? I know I need to create there a custom Slick shape so it boxes/unboxes contained model val but how should I do it (Slick docs aren't too helpful in this matter)?
I tried to do something like that based on this answer but it gives me compile error as I taken this from earlier Slick version and apparently I don't understand what is going on here.
def * = (id, name, description) <> (
(id, name, description) => BaseModel[Ingredient](id, Ingredient(name, description)),
(f: BaseModel[Ingredient]) => Some((f.id, f.name, f.description))
)
Hopefully I'm looking for something more automatic (overriden tupled, unapply in BaseModel?) but anything working and any help at all appreciated. Even RTFM if pointed to the right place.
EDIT: JimN suggested an answer which works but needs quite a lot of boilerplate when creating each such mapping. Could you suggest an answer which would minimize the amount of boilerplate?
Here is what's added to the IngredientsTable:
def toBaseModel(id: Long, name: String, description: String): BaseModel[Ingredient] = {
BaseModel[Ingredient](id, Ingredient(name, description))
}
def fromBaseModel(m: BaseModel[Ingredient]): Option[(Long, String, String)] = {
Some((m.id, m.name, m.description))
}
def * = (id, name, description) <> ((toBaseModel _).tupled, fromBaseModel)
This compiles for me:
def * = (id, name, description) <> ((toBaseModelIngredient _).tupled, fromBaseModelIngredient)
...
def toBaseModelIngredient(id: Long, name: String, description: String): BaseModel[Ingredient] = ??? // implement this
def fromBaseModelIngredient(m: BaseModel[Ingredient]): Option[(Long, String, String)] = ??? // implement this

Higher order functions with Scala Slick for DRY goodness

I have an idea how my data access layer with Scala Slick should look like, but I'm not sure if it's really possible.
Let's assume I have a User table which has the usual fields like id, email, password, etc.
object Users extends Table[(String, String, Option[String], Boolean)]("User") {
def id = column[String]("id", O.PrimaryKey)
def email = column[String]("email")
def password = column[String]("password")
def active = column[Boolean]("active")
def * = id ~ email ~ password.? ~ active
}
And I wish to query them in different ways, currently the ugly way is to have a new database session, do the for comprehension and then do different if statements to achieve what I want.
e.g.
def getUser(email: String, password: String): Option[User] = {
database withSession { implicit session: Session =>
val queryUser = (for {
user <- Users
if user.email === email &&
user.password === password &&
user.active === true
} //yield and map to user class, etc...
}
def getUser(identifier: String): Option[User] = {
database withSession { implicit session: Session =>
val queryUser = (for {
user <- Users
if user.id === identifier &&
user.active === true
} //yield and map to user class, etc...
}
What I would prefer is to have a private method for the query and then public methods which define queries along the lines of
type UserQuery = User => Boolean
private def getUserByQuery(whereQuery: UserQuery): Option[User] = {
database withSession { implicit session: Session =>
val queryUser = (for {
user <- Users
somehow run whereQuery here to filter
} // yield and boring stuff
}
def getUserByEmailAndPassword(email, pass){ ... define by query and call getUserByQuery ...}
getUserById(id){….}
getUserByFoo{….}
That way, the query logic is encapsulated in the relevant public functions and the actual querying and mapping to the user object is in a reusable function that other people dont need to be concerned with.
The problem I have is trying to refactor the "where" bit's into functions that I can pass around. Trying to do things like select them in intellij and using the refactoring results in some pretty crazy typing going on.
Does anyone have any examples they could show of doing close to what I am trying to achieve?
1) wrapping queries in a def means the query statement is re-generated on every single request, and, since query params are not bound, no prepared statement is passed to the underlying DBMS.
2) you're not taking advantage of composition
Instead, if you define parameterized query vals that def query wrappers call, you can get the best of both worlds.
val uBase = for{
u <- Users
ur <- UserRoles if u.id is ur.UserID
} yield (u,ur)
// composition: generates prepared statement one time, on startup
val byRole = for{ roleGroup <- Parameters[String]
(u,ur) <- uBase
r <- Roles if(r.roleGroup is roleGroup) && (r.id is ur.roleID)
} yield u
def findByRole(roleGroup: RoleGroup): List[User] = {
db withSession { implicit ss:SS=>
byRole(roleGroup.toString).list
}
}
If you need one-off finders for a single property, use:
val byBar = Foo.createFinderBy(_.bar)
val byBaz = Foo.createFinderBy(_.baz)
Can't remember where, maybe on SO, or Slick user group, but I did see a very creative solution that allowed for multiple bound params, basically a createFinderBy on steroids. Not so useful to me though, as the solution was limited to a single mapper/table object.
At any rate composing for comprehensions seems to do what you're trying to do.
I have recently done something similar, one way to do this could be following, write a general select method which takes a predicate
def select(where: Users.type => Column[Boolean]): Option[User] = {
database withSession { implicit session: Session =>
val queryUser = (for {
user <- Users where(user)
} //yield and map to user class, etc...
}
and then write the method which passes the actual predicate as a higher order function
def getUserByEmail(email:String):Option[User]={
select((u: Users.type) => u.*._2 === email)
}
similarly
def getActiveUserByEmail(email:String):Option[User]={
select((u: Users.type) => u.*._2 === email && u.*._4 === true)
}