How to use select statement using slick? - postgresql

I wanted to query data from example table like below using slick
| id | username | password |
+-----+----------+------------+
| 1 | admin | admin#123 |
| 2 | user | user#123 |
The query is
SELECT password FROM users WHERE username = 'user';
I have read many slick examples on stack overflow but they are more complex queries
I want a simple query as above using slick .

You can just use plain SQL query for such cases:
sql"""SELECT password FROM users WHERE username = 'user'""".as[String]
This is less portable than building queries, but let you write any arbitrary query and run it without the risk of SQL injection. Though if you are using only such queries then you don't have a reason to use Slick in the first place - you could rather use Quill or Doobie (or both) or ScalikeJDBC or jOOQ.

Using slick you can write your implementation like below:
trait UsertDAO {
this: DbComponent =>
import driver.api._
case class User(id: Int, username: String, password: String)
val userTableQuery = TableQuery[UserTable]
class UserTable(tag: Tag) extends Table[User](tag, "user") {
val id = column[String]("id", O.SqlType("NUMBER"))
val username = column[String]("username", O.SqlType("VARCHAR(200)"))
val password = column[String]("password", O.SqlType("VARCHAR(200)"))
def pk: PrimaryKey = primaryKey("pk", id)
def * : ProvenShape[CjTable] =
(id, username, password) <> (User.tupled, User.unapply)
}
}
The above class defines your table structure. Later you can define your method like to run the sql query. :
def getPassword(userName: String): Future[Option[String]] = {
db.run{
userTableQuery.filter(_.username === userName).map(_.password).to[List].result.headOption
}
}
If you still face any issues, Try looking at the CRUD application i made: https://github.com/SangeetaGulia/Student-CRUD-Slick/blob/master/Student-Slick-Project.zip

First define a class that represents your SQL table
class UsersTable(tag: Tag) extends Table[User](tag, "users") {
def email: Rep[String] = column[String]("email", O.PrimaryKey)
def password: Rep[String] = column[String]("password")
def registered: Rep[Boolean] = column[Boolean]("registered")
def firstName: Rep[Option[String]] = column[Option[String]]("first_name")
def lastName: Rep[Option[String]] = column[Option[String]]("last_name")
def username: Rep[Option[String]] = column[Option[String]]("username", O.Unique)
def contact: Rep[Option[String]] = column[Option[String]]("contact")
override def * = (email, password, registered, firstName, lastName, username, contact) <> (User.tupled, User.unapply)
}
Then create a DAO for your table which has the method to select 'one' row. Like this:
class UsersDao #Inject()(protected val dbConfigProvider: DatabaseConfigProvider)
(implicit executionContext: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
private val Users = TableQuery[UsersTable]
def selectOne(email: String): Future[Option[User]] = db.run(Users.filter(_.email === email).result.headOption)
}
Now in your service class read the record like this
class AuthService #Inject()(usersDao: UsersDao) {
def someFunction(.. some_params ..) {
usersDao.selectOne(someParama.email).flatMap {
case Some(user: User) => //Do something if user exists
case None => //Do something else if user doesn't exist
}
}
}
In case you're interested in exploring a play project written in Scala, have a look at this project https://github.com/ashishtomer/fastscraping-web

Related

Getting execution exception for custom slick profile

Im' getting an exception when I try to use my own custom profile with slick. The reason why I want to use it is because I want to keep JSON in my postgresql database. Therefore, I'm using pg-slick.
The exception says:
slick.jdbc.PostgresProfile$ cannot be cast to util.ExtendedPostgresProfile.
This is my code for the ExtendedPostgresProfile:
package util
import com.github.tminglei.slickpg._
trait ExtendedPostgresProfile extends ExPostgresProfile with PgPlayJsonSupport {
override val api = new API with PlayJsonImplicits
override def pgjson: String = "jsonb"
}
object ExtendedPostgresProfile extends ExtendedPostgresProfile
This is my DAO class:
class ActivityDAO #Inject()(dbConfigProvider: DatabaseConfigProvider)(implicit ec: ExecutionContext) {
private val dbConfig = dbConfigProvider.get[ExtendedPostgresProfile]
import dbConfig._
import profile.api._
private class ActivityTable(tag: Tag) extends Table[Activity](tag, "activity") {
def id: Rep[Long] = column[Long]("id", O.PrimaryKey, O.AutoInc)
def activity: Rep[JsValue] = column[JsValue]("activity")
def atTime: Rep[Timestamp] = column[Timestamp]("at_time")
def activityTypeId: Rep[Int] = column[Int]("activiry_type_id")
def userId: Rep[Long] = column[Long]("user_id")
override def * : ProvenShape[Activity] =
(id.?, activity, atTime.?, activityTypeId, userId.?) <> ((Activity.apply _).tupled, Activity.unapply)
}
private val activities = TableQuery[ActivityTable]
def add(activity: Activity): Future[Long] = {
val query = activities returning activities.map(_.id)
db.run(query += activity)
}
def filter(userId: Long): Future[Seq[Activity]] = {
db.run(activities.filter(_.userId === userId).result)
}
}
I've tried searching for the answer my self, but haven't had much luck.
Is your custom profile configured in your Play-slick configuration as suggested at the Database Configuration section? I.e. is it util.ExtendedPostgresProfile$ or is it slick.jdbc.PostgresProfile$?

Play Framework and Slick: testing database-related services

I'm trying to follow the most idiomatic way to having a few fully tested DAO services.
I've got a few case classes such as the following:
case class Person (
id :Int,
firstName :String,
lastName :String
)
case class Car (
id :Int,
brand :String,
model :String
)
Then I've got a simple DAO class like this:
class ADao #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
private val persons = TableQuery[PersonTable]
private val cars = TableQuery[CarTable]
private val personCar = TableQuery[PersonCar]
class PersonTable(tag: Tag) extends Table[Person](tag, "person") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def firstName = column[String]("name")
def lastName = column[String]("description")
def * = (id, firstName, lastName) <> (Person.tupled, Person.unapply)
}
class CarTable(tag: Tag) extends Table[Car](tag, "car") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def brand = column[String]("brand")
def model = column[String]("model")
def * = (id, brand, model) <> (Car.tupled, Car.unapply)
}
// relationship
class PersonCar(tag: Tag) extends Table[(Int, Int)](tag, "person_car") {
def carId = column[Int]("c_id")
def personId = column[Int]("p_id")
def * = (carId, personId)
}
// simple function that I want to test
def getAll(): Future[Seq[((Person, (Int, Int)), Car)]] = db.run(
persons
.join(personCar).on(_.id === _.personId)
.join(cars).on(_._2.carId === _.id)
.result
)
}
And my application.conf looks like:
slick.dbs.default.driver="slick.driver.PostgresDriver$"
slick.dbs.default.db.driver="org.postgresql.Driver"
slick.dbs.default.db.url="jdbc:postgresql://super-secrete-prod-host/my-awesome-db"
slick.dbs.default.db.user="myself"
slick.dbs.default.db.password="yolo"
Now by going through Testing with databases and trying to mimic play-slick sample project
I'm getting into so much trouble and I cannot seem to understand how to make my test use a different database (I suppose I need to add a different db on my conf file, say slick.dbs.test) but then I couldn't find out how to inject that inside the test.
Also, on the sample repo, there's some "magic" like Application.instanceCache[CatDAO] or app2dao(app).
Can anybody point me at some full fledged example of or repo that deals correctly with testing play and slick?
Thanks.
I agree it's confusing. I don't know if this is the best solution, but I ended up having a separate configuration file test.conf that specifies an in-memory database:
slick.dbs {
default {
driver = "slick.driver.H2Driver$"
db.driver = "org.h2.Driver"
db.url = "jdbc:h2:mem:play-test"
}
}
and then told sbt to use this when running the tests:
[..] javaOptions in Test ++= Seq("-Dconfig.file=conf/test.conf")

Why does compilation fail with "not found: value Users"?

I want to retrieve a row from my default database postgres. I have table "Users" defined already.
conf/application.conf
db.default.driver=org.postgresql.Driver
db.default.url="jdbc:postgresql://localhost:5234/postgres"
db.default.user="postgres"
db.default.password=""
controllers/Application.scala
package controllers
import models.{UsersDatabase, Users}
import play.api.mvc._
object Application extends Controller {
def index = Action {
Ok(views.html.index(UsersDatabase.getAll))
}
}
models/Users.scala
package models
import java.sql.Date
import play.api.Play.current
import play.api.db.DB
import slick.driver.PostgresDriver.simple._
case class User(
id: Int,
username: String,
password: String,
full_name: String,
email: String,
gender: String,
dob: Date,
joined_date: Date
)
class Users(tag: Tag) extends Table[User](tag, "Users") {
def id = column[Int]("id")
def username = column[String]("username", O.PrimaryKey)
def password = column[String]("password")
def full_name = column[String]("full_name")
def email = column[String]("email")
def gender = column[String]("gender")
def dob = column[Date]("dob")
def joined_date = column[Date]("joined_date")
def * = (id, username, password, full_name, email, gender, dob, joined_date) <> (User.tupled, User.unapply)
}
object UsersDatabase {
def getAll: List[User] = {
Database.forDataSource(DB.getDataSource()) withSession {
Query(Users).list
}
}
}
While accessing http://localhost:9000/ it gives compilation error:
[error] .../app/models/Users.scala:36: not found: value Users
[error] Query(Users).list
[error] ^
[error] one error found
[error] (compile:compile) Compilation failed
How to resolve this error and access data properly?
The compilation error message says it all - there's no value Users to use in the scope.
Change the object UsersDatabase to look as follows:
object UsersDatabase {
val users = TableQuery[Users]
def getAll: List[User] = {
Database.forDataSource(DB.getDataSource()) withSession { implicit session =>
users.list
}
}
}
And the error goes away since you're using the local val users to list users in the database.
As described in Querying in the official documentation of Slick session val is an implicit value of list (as final def list(implicit session: SessionDef): List[R]), and hence implicit session in the block:
All methods that execute a query take an implicit Session value. Of
course, you can also pass a session explicitly if you prefer:
val l = q.list(session)

Asymmetric table projection in Slick

Is it possible to have slick have asymmetric projects for select * vs insert? For example, this is what I'd like to do:
class Users(tag: Tag)
extends Table[(String, String)](tag, "users") {
def username = column[String]("username", O.PrimaryKey)
def email = column[String]("email", O.NotNull)
def password = column[String]("password", O.NotNull)
def * = (username, email)
}
val users = TableQuery[Users]
users += ("username", "email", "password")
println(users.take(1).firstOption) // prints ("username, "email)
Is there any practical way of making this work? The main issue is the insert, complaining about too many parameters because it doesn't match the projection.
Untested, but how about:
def withPassword = (username, email, password)
and then use as:
users.withPassword += ("username", "email", "password")
I believe that's the general approach to overloading the * projection.

Scala Slick Model with Optional Image column

I am trying to add a column to an existing model that has an optional image, I am using Scala Slick with the Play Framework. What would be an example of storing a image with a Scala Slick model?
case class Complication(complicationId:Long,
vitalSignId:Long,
complication:String,definition:String,reason:String,
treatment:String,notes:String,
weblinks:String,upperlower:String)
object ComplicationDAO extends Table[Complication]("complications") with DbConn {
def complicationId = column[Long]("complication_id", O.PrimaryKey, O.AutoInc)
def vitalSignId = column[Long]("vital_sign_id")
def complication = column[String]("complication")
def definition = column[String]("definition",O.DBType("varchar(4000)"))
def reason = column[String]("reason",O.DBType("varchar(4000)"))
def treatment = column[String]("treatment",O.DBType("varchar(4000)"))
def notes = column[String]("notes",O.DBType("varchar(4000)"))
def weblinks = column[String]("weblinks")
def upperlower = column[String]("upperlower")
def treatmentImage = ??? //here is where it needs to be defined!
def * = complicationId ~ vitalSignId ~ complication ~ definition ~ reason ~ treatment ~ notes ~ weblinks ~ upperlower<> (Complication, Complication.unapply _)
def autoInc =
vitalSignId ~ complication ~ definition ~ reason ~
treatment ~ notes ~ weblinks ~ upperlower <>
(NewComplication, NewComplication.unapply _) returning complicationId
}
This is the model that I am trying to integrate an image into.
Thanks for the advice!
If you don't want to actually store it into the DB you can use a url as String field, this example can get you started.
A table for pictures is used:
case class Picture(url: String, id: Option[Int] = None)
trait PictureComponent { this: Profile => //requires a Profile to be mixed in...
import profile.simple._ //...to be able import profile.simple._
class Pictures(tag: Tag) extends Table[Picture](tag, "PICTURES") {
def id = column[Option[Int]]("PIC_ID", O.PrimaryKey, O.AutoInc)
def url = column[String]("PIC_URL", O.NotNull)
def * = (url, id) <> (Picture.tupled, Picture.unapply)
}
val pictures = TableQuery[Pictures]
private val picturesAutoInc = pictures returning pictures.map(_.id) into { case (p, id) => p.copy(id = id) }
def insert(picture: Picture)(implicit session: Session): Picture = picturesAutoInc.insert(picture)
}
And then the user table as an external reference to the picture id:
class Users(tag: Tag) extends Table[(String, Int, Option[Int])](tag, "USERS") {
def id = column[Option[Int]]("USER_ID", O.PrimaryKey, O.AutoInc)
def name = column[String]("USER_NAME", O.NotNull)
def pictureId = column[Int]("PIC_ID", O.NotNull)
def * = (name, pictureId, id)
}
When inserting you can upload the image, store the url, retrieve the id and assign it to the User entity as external key. To make the external key column nullable you just have to use [column[Option[Int]] instead of column[Int].
Otherwise you could use a Blob field as suggested in a comment, there's a test inside the Slick library with JDBC that creates a table with a Blob field:
class T(tag: Tag) extends Table[(Int, Blob)](tag, "test3") {
def id = column[Int]("id")
def data = column[Blob]("data")
def * = (id, data)
}
val ts = TableQuery[T]
And then to insert:
ts insert (1, new SerialBlob(Array[Byte](1,2,3)))
ts insert (2, new SerialBlob(Array[Byte](4,5)))
Then you can look into java.io.serializable on how to transform a file to bites before storing it into a Blob, this link could help (ignore the sql parts).