I have the following code in Scala:
case class Product(id: Option[Long] = None, name: String, price: BigDecimal, description: String)
object Products extends Table[Product]("product") {
def id = column[Long]("id", O.AutoInc, O.PrimaryKey)
def name = column[String]("name", O.NotNull)
def price = column[BigDecimal]("price", O.NotNull)
def description = column[String]("description", O.NotNull)
def * = id.? ~ name ~ price ~ description <>(Product.apply _, Product.unapply _)
def autoInc = * returning id
def add(product: Product)(implicit s:Session): Long = {
Products.autoInc.insert(product)
}
def all(implicit s:Session): List[Product] = {
Query(Products).list
}
}
Listing all products works great, however, I can't make adding method working.
After calling:
val myProduct = models.Product(id = None, name = "test2", price = BigDecimal(2.99), description = "test3")
models.Products.add(myProduct)
I constanty get an error message from PostgreSQL saying that id cannot be null. I totally agree with that, but why the id column is not being set by autoInc? Doesn't it work that way?
I use Play! 2.1.2, Scala 2.10.0, PostgreSQL 9.3 and play-slick 0.3.3.
Thanks in advance.
Here is a suggestion, rewrite your autoInc and add methods like this:
def autoInc = name ~ price ~ description returning id
def add(product: Product)(implicit s:Session): Long = {
Products.autoInc.insert(p.name, p.price, p.description)
}
Some databases own't allow you to insert null in the auto increment column. Maybe it's the Postgres case.
Related
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
Imagine the following scenario: You have a book that consists of ordered chapters.
First the test:
"Chapters" should "have a unique order" in
{
// val exception = intercept
db.run(
DBIO.seq
(
Chapters.add(0, 0, "Chapter #0"),
Chapters.add(0, 0, "Chapter #1")
)
)
}
Now the implementation:
case class Chapter(id: Option[Long] = None, bookId: Long, order: Long, val title: String) extends Model
class Chapters(tag: Tag) extends Table[Chapter](tag, "chapters")
{
def id = column[Option[Long]]("id", O.PrimaryKey, O.AutoInc)
def bookId = column[Long]("book_id")
def order = column[Long]("order")
def title = column[String]("title")
def * = (id, bookId, order, title) <> (Chapter.tupled, Chapter.unapply)
def uniqueOrder = index("order_chapters", (bookId, order), unique = true)
def bookFK = foreignKey("book_fk", bookId, Books.all)(_.id.get, onUpdate = ForeignKeyAction.Cascade, onDelete = ForeignKeyAction.Restrict)
}
Maybe such a unique-constraint on 2 columns isn't even possible in h2?
Anyway:
Expectation:
An exception to be thrown that I can then intercept/expect in my test, hence a failing test for now, for violating a unique-constraint.
Actual result:
A successful test :(
edit: Also, I use this:
implicit val defaultPatience =
PatienceConfig(timeout = Span(30, Seconds), interval = Span(100, Millis))
db.run returns a Future.
You have to Await on it to get the result of the execution.
Try this:
import scala.concurrent.duration._
val future = db.run(...)
Await.result(future, 5 seconds)
This requirement should be really easy, but I don't know why is not working. I want to delete a row based on it's id using slick with play framework.
I'm following this example from play-slick module, but compiler complains that value delete is not a member of scala.slick.lifted.Query[models.Tables.MyEntity,models.Tables.MyEntity#TableElementType].
My controller looks like:
def delete(id: Int) = DBAction{ implicit rs =>
val q = MyEntity.where(_.id === id)
q.delete
Ok("Entity deleted")
}
I've imported the play.api.db.slick.Config.driver.simple._
What am I doing wrong?
Edit:
My schema definition looks like:
class Cities(tag: Tag) extends Table[CityRow](tag, "cities") {
def * = (cod, name, state, lat, long, id) <> (CityRow.tupled, CityRow.unapply)
/** Maps whole row to an option. Useful for outer joins. */
def ? = (cod.?, name.?, state.?, lat, long, id.?).shaped.<>({r=>import r._; _1.map(_=> CityRow.tupled((_1.get, _2.get, _3.get, _4, _5, _6.get)))}, (_:Any) => throw new Exception("Inserting into ? projection not supported."))
val cod: Column[String] = column[String]("cod")
val name: Column[String] = column[String]("name")
val state: Column[Int] = column[Int]("state")
val lat: Column[Option[String]] = column[Option[String]]("lat")
val long: Column[Option[String]] = column[Option[String]]("long")
val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
/** Foreign key referencing Departamentos (database name fk_ciudades_departamentos1) */
lazy val stateFk = foreignKey("fk_cities_states", state, States)(r => r.idstate, onUpdate=ForeignKeyAction.NoAction, onDelete=ForeignKeyAction.NoAction)
}
I also had a look at that example some time ago and it looked wrong to me too, not sure wether I was doing something wrong myself or not, the delete function was always a bit tricky to get right, expecially using the lifted.Query (like you are doing). Later in the process I made it work importing the right drivers, in my case scala.slick.driver.PostgresDriver.simple._.
Edit after comment:
Probably you have an error in the shape function, hard to say without looking at your schema declaration. This is an example:
case class CityRow(id: Long, name: String) {
class City(tag: Tag) extends Table[CityRow](tag, "city") {
def * = (id, name) <>(CityRow.tupled, CityRow.unapply)
^this is the shape function.
def ? = (id.?, name).shaped.<>({
r => import r._
_1.map(_ => CityRow.tupled((_1.get, _2)))
}, (_: Any) => throw new Exception("Inserting into ? projection not supported."))
val id: Column[Long] = column[Long]("id", O.AutoInc, O.PrimaryKey)
val name: Column[String] = column[String]("name")
}
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).
I have table definition in Slick:
object ADB {
extends BaseDB[A]("a")
with PostgresDriver{
def id = column[Long]("id", O.PrimaryKey)
def name = column[String]("name")
...
def * = id ~ name ~ ... <> (A.apply _, A.unapply _)
def forSelect = id ~ name
}
Is it possible to refer to forSelect when querying for A?
I want to keep the list of field to be selected in one place to be able to push forSelect to trait in future.
I believe you can accomplish what you want like this:
( for( a <- ADB ) yield a.forSelect ).list
The difference between this and what stefan.schwetschke posted is that I'm using the instance a to reference forSelect instead of accessing it from the ADB object itself.
Just try
...map(ADB.forSelect)
or
for( ... ) yield ADB.forSelect
The following worked for me:
import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession
object ADB extends Table[(Long, String)]("a") {
def id = column[Long]("id", O.PrimaryKey)
def name = column[String]("name")
def * = id ~ name
def forSelect = id ~ name
}
Database.forURL("jdbc:h2:mem:test1", driver = "org.h2.Driver") withSession {
( for( a <- ADB ) yield ADB.forSelect ).list
}