Good Morning,
I've got the following code snippet:
def createResponsibleProcessTemplates(processTemplateId: Int, username: String): Future[Int] = db.run {
println("Create Responsible TemplateId: " + processTemplateId + " UserName: " + username)
(responsibleProcessTemplates
returning responsibleProcessTemplates.map(_.processTemplateId)
) += ResponsibleProcessTemplateModel(processTemplateId, username)
}
My Model is:
package models
import play.api.libs.json.Json
case class ResponsibleProcessTemplateModel(
processTemplateId: Int,
username: String)
object ResponsibleProcessTemplateModel {
implicit val responsibleProcessTemplateFormat = Json.format[ResponsibleProcessTemplateModel]
}
And the trait looks like this:
trait ResponsibleProcessTemplateComponent {
self: HasDatabaseConfigProvider[JdbcProfile] =>
import profile.api._
class ResponsibleProcessTemplate(tag: Tag) extends Table[ResponsibleProcessTemplateModel](tag, "Responsible_ProcessTemplates") {
def processTemplateId: Rep[Int] = column[Int]("processTemplateId")
def username: Rep[String] = column[String]("username")
def * : ProvenShape[ResponsibleProcessTemplateModel] = (processTemplateId, username) <> ((ResponsibleProcessTemplateModel.apply _).tupled, ResponsibleProcessTemplateModel.unapply)
}
val responsibleProcessTemplates: TableQuery[ResponsibleProcessTemplate] = TableQuery[ResponsibleProcessTemplate]
}
It should insert some data into a database table without generating an autoincerement id.
I don't get errors, but data is not stored in database.
Thanks for your help.
I found the solution!
def createResponsibleProcessTemplates(processTemplateId: Int, username: String): Future[Int] = {
db.run(responsibleProcessTemplates.map(x => (x.processTemplateId, x.username)) += (processTemplateId, username))
}
Related
class MyTable(tag: Tag) extends Table[MyEntity](tag, "1970Table") {
def id = column[Int]("id")
override def * =
(
id
) <> (MyEntity.tupled, MyEntity.unapply)
}
val myTable = TableQuery[MyTable]
class MyRepository(val config: DatabaseConfig[JdbcProfile])
extends MyRepository[MyTable, String] {
override val table: config.profile.api.TableQuery[MyTable] = myTable
def insert(me: MyEntity): Future[Int] = {
db.run(table += me)
}
}
I use this in my other classes like this:
val myRepository = new MyRepository(dbConfig)
myRepository.insert(myrecord)
Question
I would like to not have a hardcoded tablename but rather make the tablename dynamic.
I would like to change the insert method such that it accepts a year (int) parameter and based on the year parameter it chooses the right table. i.e. if the year passed in is 1970 then table name is 1970Table but if the year passed in is 1980 then the table is 1980Table.
Try
class MyRepository(val config: DatabaseConfig[JdbcProfile]) {
import config._
import profile.api._
abstract class MyTable(tag: Tag, name: String) extends Table[MyEntity](tag, name) {
def id = column[Int]("id")
override def * = (id) <> (MyEntity.tupled, MyEntity.unapply)
}
class Table1970(tag: Tag) extends MyTable[MyEntity](tag, "1970Table")
class Table1980(tag: Tag) extends MyTable[MyEntity](tag, "1980Table")
val table1970 = TableQuery[Table1970]
val table1980 = TableQuery[Table1980]
def insert(me: MyEntity, year: Int): Future[Int] = db.run {
year match {
case "1970" => table1970 += me
case "1980" => table1980 += me
}
}
}
Now
val myRepository = new MyRepository(dbConfig)
myRepository.insert(myrecord, "1970")
There is two apply methods in TableQuery. val myTable = TableQuery[MyTable] -
this one uses macros to create MyTable.
The other one is defined like this:
def apply[E <: AbstractTable[_]](cons: Tag => E): TableQuery[E] =
new TableQuery[E](cons)
So you can do smth like this
class MyTable(tag: Tag, tableName: String) extends Table[MyEntity](tag, tableName)
...
def myTable(name: String) = TableQuery[MyTable](tag => new MyTable(tag, name))
Now you can predefine all tables you need and use them or do smth like this
class MyRepository(val config: DatabaseConfig[JdbcProfile])
extends MyRepository[MyTable, String] {
override def table(year: Int): config.profile.api.TableQuery[MyTable] = myTable(year.toString)
def insert(me: MyEntity, year: Int): Future[Int] = {
db.run(table(year) += me)
}
}
How can I get a collection of JurisdictionRow objects? I need a SELECT * FROM jurisdiction
object JurisdictionRepo extends {
val profile = slick.driver.MySQLDriver
} with JurisdictionRepo
trait JurisdictionRepo {
private val dbConfig: DatabaseConfig[MySQLDriver] = DatabaseConfig.forConfig("pnga-master-data")
private val db = dbConfig.db
val profile: slick.driver.JdbcProfile
val tableName = "jurisdiction"
def add(jurisdictionRow: JurisdictionRow): Future[Unit] = db.run(query += jurisdictionRow).map { _ => () }
def delete(id: String): Future[Int] = db.run(query.filter(_.id === id).delete)
def get(id: String): Future[Option[JurisdictionRow]] = db.run(query.filter(_.id === id).result.headOption)
def all() = ???
import profile.api._
lazy val schema: profile.SchemaDescription = query.schema
case class JurisdictionRow(id: String,
parentId: String,
name: String,
code: String)
class Jurisdiction(_tableTag: Tag) extends Table[JurisdictionRow](_tableTag, tableName) {
val id: Rep[String] = column[String](s"${tableName}_id", O.PrimaryKey, O.Length(36, varying=true))
val parentId: Rep[String] = column[String]("parent_id", O.Length(36, varying=true))
val name: Rep[String] = column[String]("name", O.Length(255, varying=true))
val code: Rep[String] = column[String]("code", O.Length(255, varying=true))
def * = (id, parentId, name, code) <> (JurisdictionRow.tupled, JurisdictionRow.unapply _)
}
lazy val query = new TableQuery(tag => new Jurisdiction(tag))
}
I would like to implement the all method to return all possible JurisdictionRow objects in the table. This seems like a common case, but the Slick documentation has not been helpful. I just need a plain old result set, no fancy filtering, etc.
Just replicate what you already have in the other queries but without the filter part.
def all = db.run(query.result)
Have look at the first example:
http://slick.lightbend.com/doc/3.2.0/gettingstarted.html#querying
I have two models:
case class User(uid: Option[Int], email: String, password: String, created_at: Timestamp, updated_at: Timestamp)
case class UserProfile(firstname: String, lastname: String, gender: Int, user_id: Long),
And a DAO with user table defined:
package dao
import java.sql.Timestamp
import scala.concurrent.{Await, Future}
import javax.inject.Inject
import models.{User, UserProfile}
import play.api.db.slick.DatabaseConfigProvider
import play.api.db.slick.HasDatabaseConfigProvider
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import slick.driver.JdbcProfile
import slick.profile.SqlProfile.ColumnOption.SqlType
import scala.concurrent.duration._
import com.github.t3hnar.bcrypt._
class UsersDAO #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
private val Users = TableQuery[UsersTable]
private val UsersProfile = TableQuery[UserProfileTable]
def all(): Future[Seq[User]] = db.run(Users.result)
def insert(user: User): Future[Int] = {
println("coming inside insert of user dao")
println(user)
// insertUP(user)
val hashPassword = user.password.bcrypt
val updatedUser = user.copy(password = hashPassword)
val query = db.run((Users returning Users.map(_.uid)) += updatedUser)
// val uid = Await.result(query, 30 seconds)
// println(s"UID ---------> $uid")
query
}
def findByEmail(email: String): Option[User] = {
val query = for {
u <- Users if u.email === email
} yield u
val f: Future[Option[User]] = db.run(query.result).map(_.headOption)
val result = Await.result(f, 30 seconds)
println(result.isDefined)
result
}
def authenticate(username: String, password: String): Future[Option[User]] = {
val query = db.run(Users.filter(_.email === username).result.map(_.headOption.filter(user => password.isBcrypted(user.password)))).map(_.headOption)
query
}
private class UsersTable(tag: Tag) extends Table[User](tag, "users") {
def uid = column[Int]("uid", O.PrimaryKey, O.AutoInc, O.SqlType("INT"))
def email = column[String]("email")
def password = column[String]("password")
def created_at = column[Timestamp]("created_at", SqlType("timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP"))
def updated_at = column[Timestamp]("updated_at", SqlType("timestamp not null default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP"))
def idx = index("email_UNIQUE", email, unique = true)
def * = (uid.?, email, password, created_at, updated_at) <> (User.tupled, User.unapply _)
}
private class UserProfileTable(tag: Tag) extends Table[UserProfile](tag, "user_profile"){
def firstname = column[String]("firstname")
def lastname = column[String]("lastname")
def gender = column[Int]("gender")
def user_id = column[Int]("user_id")
def * = (firstname, lastname, gender, user_id) <> (UserProfile.tupled, UserProfile.unapply)
def fk_user_id = foreignKey("fk_user_id", user_id, Users)(_.uid)
}
}
In the insert function, how could I add uid to user_id field of user profile table in the same function or call?
Edit 1
Tried the solution suggested by Pawel, but getting exception:
failed slick.SlickException: This DBMS allows only a single AutoInc column to be returned from an INSERT
Edit - 2
Now, after trying some solutions, the insert function is like this:
def insert(user: User): Future[Int] = {
val hashPassword = user.password.bcrypt
val updatedUser = user.copy(password = hashPassword)
val insertUser = (Users returning Users.map(_.uid)) += updatedUser
def insertUserProfile(updatedUserProfile: UserProfile) = (UsersProfile returning UsersProfile.map(_.user_id)) += updatedUserProfile
val insertUserThenProfile = for {
createdUserId <- insertUser
createdUserProfileId <- insertUserProfile(UserProfile("First name", "Last name", gender = 0, user_id = createdUserId))
} yield createdUserProfileId
db.run(insertUserThenProfile.transactionally)
}
But still getting the error:
failed slick.SlickException: This DBMS allows only a single AutoInc column to be returned from an INSERT
Solution
Pawels solution should work fine, but some DBMS gives exception for not returning AUtoInc field, and for trying to return something else.
You can see the note in documentation:
http://slick.lightbend.com/doc/3.0.0/queries.html
Note
Many database systems only allow a single column to be returned which must be the table’s auto-incrementing primary key. If you ask for other columns a SlickException is thrown at runtime (unless the database actually supports it).
So, Now, my models are like this:
case class User(uid: Option[Int], email: String, password: String, created_at: Timestamp, updated_at: Timestamp)
case class UserProfile(upid: Option[Int], firstname: String, lastname: String, gender: Int, user_id: Int)
And the Table class:
private class UserProfileTable(tag: Tag) extends Table[UserProfile](tag, "user_profile"){
def upid= column[Int]("upid", O.PrimaryKey, O.AutoInc, O.SqlType("INT"), O.Default(0))
def firstname = column[String]("firstname")
def lastname = column[String]("lastname")
def gender = column[Int]("gender")
def user_id = column[Int]("user_id")
def * = (upid.?, firstname, lastname, gender, user_id) <> (UserProfile.tupled, UserProfile.unapply)
def fk_user_id = foreignKey("fk_user_id", user_id, Users)(_.uid)
}
And finally the insert method:
def insert(user: User): Future[Int] = {
val hashPassword = user.password.bcrypt
val updatedUser = user.copy(password = hashPassword)
val insertUser = (Users returning Users.map(_.uid)) += updatedUser
def insertUserProfile(updatedUserProfile: UserProfile) = (UsersProfile returning UsersProfile.map(_.upid)) += updatedUserProfile
val insertUserThenProfile = for {
createdUserId <- insertUser
createdUserProfileId <- insertUserProfile(UserProfile(Some(0), "First name", "Last name", gender = 0, user_id = createdUserId))
} yield createdUserProfileId
db.run(insertUserThenProfile.transactionally)
}
I don't know how are you planning to provide values for UserProfile (it's up to you, maybe an additional parameter in the insert method), but I would try something like this:
def insert(user: User): Future[UserProfile] = {
val hashPassword = user.password.bcrypt
val updatedUser = user.copy(password = hashPassword)
val insertUser = (Users returning Users.map(_.uid)) += updatedUser
def insertUserProfile(updatedUserProfile: UserProfile) = (UsersProfile returning UsersProfile.map(_.user_id)) += updatedUserProfile
val insertUserThenProfile = for {
createdUserId <- insertUser
createdUserProfileId <- insertUserProfile(UserProfile("First name", "Last name", gender = 0, user_id = createdUserId))
} yield createdUserProfileId
db.run(insertUserThenProfile.transactionally)
}
Make a query to insert a new Member and return the member with its id
val memberWithId = (queryMember returning queryMember.map(_.id) into ((c, id) => c.copy(id = id))) += registerMember.copy(password = pwHash)
Then implement the query as a transaction. Insert Member, insert User Profile(you can use the id from the success of the first statement)
val transaction = (for {
m: Member <- memberWithId
p: UsersProfile <- queryProfile returning queryProfile += MemberProfile(m.id, firstName, ...)
} yield m).transactionally
Run it as a Try so that you can more easily parse errors rather than crashing.
db.run(transaction.asTry)
I have AccountTable:
import models.{Account, Category}
import play.api.Play
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfig}
import slick.driver.JdbcProfile
import slick.driver.PostgresDriver.api._
import slick.lifted.Tag
import play.api.libs.json.{JsValue, Writes, Json}
object AccountTable extends AccountTable
trait AccountTable extends HasDatabaseConfig[JdbcProfile]{
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
class Accounts(tag: Tag) extends Table[Account](tag, "account") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def login = column[String]("login")
def password = column[String]("password")
def * = (id, login, password) <> ((Account.apply _).tupled, Account.unapply)
}
val accounts = TableQuery[Accounts]
implicit val accountFormat = Json.format[Account]
def auth(login: String, password: String): Boolean = {
db.run(findByLogin(login, password).result.headOption)
}
private def findByLogin(login: String, password: String) = accounts.filter(x => (x.password === password && x.login === login))
}
I try to develop auth method. I really don't understand, how to complete this method. I tried different ways, but always I get different errors.
Option 1:
def auth(login: String, password: String): Future[Boolean] = {
val action = findByLogin(login, password).result.headOption
.map(authOpt => authOpt.getOrElse(false))
db.run(action)
}
Option 2:
def auth(login: String, password: String): Boolean = {
val action = findByLogin(login, password).result.headOption
.map(authOpt => authOpt.getOrElse(false))
Await.result(db.run(action), 5 seconds)
}
The code above is untested, but you should get the idea behind queries in slick. I assumed that auth should return false if no result was found in the db.
One last word: I strongly recommend to use option 1 and work with Future, as Await.result blocks the thread from execution.
I've used the Slick 3.1 code generator to create the default object and trait Tables.scala
The below method works however I would like to implicitly or explicitly convert UserRow and PasswordsRow to User and UserPassword.
Working method:
override def getUser(email: String): Future[Option[(Tables.UsersRow, Tables.PasswordsRow)]] = db.run {
(for {
user <- users if user.email === email
password <- passwords if password.id === user.id
} yield (user, password)).result.headOption
}
Desired method:
override def getUser(email: String): Future[Option[(User, UserPassword)]] = db.run {
(for {
user <- users if user.email === email
password <- passwords if password.id === user.id
} yield (user, password)).result.headOption
}
User.scala
package model
import com.wordnik.swagger.annotations.{ ApiModel, ApiModelProperty }
import slick.jdbc.GetResult
import spray.json.DefaultJsonProtocol
import scala.annotation.meta.field
case class User(
id: Int,
email: String,
name: Option[String] = None,
surname: Option[String] = None,
passwordId: Option[Int] = None
)
object User extends DefaultJsonProtocol{
implicit val getUserResult = GetResult(r => User(r.<<, r.<<, r.<<, r.<<, r.<<))
implicit val userFormat = jsonFormat5(User.apply)
}
UserPassword.scala
package model
import com.github.t3hnar.bcrypt.{Password, generateSalt}
import slick.jdbc.GetResult
case class UserPassword(id: Int, hashedPassword: Option[String], salt: String = generateSalt) {
def passwordMatches(password: String): Boolean = hashedPassword.contains(password.bcrypt(salt))
}
object UserPassword {
implicit val getUserPasswordResult = GetResult(r => UserPassword(r.<<, r.<<, r.<<))
def newWithPassword(password: String) = {
val salt = generateSalt
new UserPassword(0, Some(password.bcrypt(salt)), salt)
}
}
Someting like this maybe?
val futureUserRowAndPwdRow = getUser(email)
val futureUser: Future[Option[(User, UserPassword)]] = futureUserRowAndPwdRow map {
maybeUserRow => maybeUserRow map {
case (userRow, pwdRow) => (User(userRow.whatever....), UserPassword(..))
}
}