Higher Order Functions with Scala Slick for DRY goodness

I have an idea how my data access level should look like using Scala Slick, but I'm not sure if this is really possible.

Suppose I have a User table that has regular 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 want to query them in different ways, the currently ugly way is to have a new database session, do it for understanding, and then execute different if statements to achieve what I want.

eg.

  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... } 

I would prefer to have a private method for the query, and then public methods that define the queries row by row

 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{….} 

Thus, the request logic is encapsulated in the corresponding public functions, and the actual request and mapping to the user object is in a reusable function that other people should not bother.

I have a problem refactoring the "where" bit in a function that I can execute. Trying to do things like select them in intellij and use the refactoring results in some pretty crazy print.

Does anyone have any examples that they could show regarding what I'm trying to achieve?

+3
source share
2 answers

1) wrapping the queries in def means that the query query is re-created for each individual query and, since the query parameters are not related, no prepared statement is transferred to the base DBMS.

2) you do not use the composition

Instead, if you define parameterized vals queries that trigger a query wrapper query, 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-time crawlers for a single property, use:

 val byBar = Foo.createFinderBy(_.bar) val byBaz = Foo.createFinderBy(_.baz) 

I don’t remember where, perhaps, in the SO or the Slick user group, but I saw a very creative solution that allowed for several related parameters, mainly createFinderBy on steroids. Not very useful for me, since the solution was limited to a single mapper / table object.

In any case, compilation for understanding seems to do what you are trying to do.

+6
source

I recently did something similar, one way to do this might follow, write a general selection method that 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) } 
0
source

All Articles