Error: value <> is not a member of. Slick - scala

i new in Scala. I going through tutorial and try to create something useful, but faced with strange error like this:
value <> is not a member of (slick.lifted.Rep[Long], slick.lifted.Rep[String], slick.lifted.Rep[String], slick.lifted.Rep[String])
My code:
package models
import java.sql.Timestamp
import slick.jdbc.MySQLProfile._
import slick.jdbc.MySQLProfile.api.stringColumnType
import slick.jdbc.MySQLProfile.api.longColumnType
import slick.jdbc.MySQLProfile.api.timestampColumnType
import slick.lifted.Tag
case class User(id: Long, name: String, email: String, PMAccount: String)
class Users(tag: Tag) extends Table[User](tag, "Users") {
def id = column[Long]("id")
def name = column[String]("name")
def email = column[String]("email")
def PMAccount = column[String]("PMAccount")
def * = (id, name, email, PMAccount) <> (User.tupled, User.unapply(_))
}
Can anyone help me to understand this ?

You have forgotten to import necessary api, just add this line to your code and it should work
import database.driver.api._
def * = (id, name, email, PMAccount) <> (User.tupled, User.unapply(_))

Related

connecting slick 3.1.1 to the database

I have the following code and I'm trying to connect to the MySQL database without success.
cat Database.scala
package com.github.odnanref.EmailFilter
import slick.driver.MySQLDriver._
import slick.driver.MySQLDriver.backend.Database
/**
* Created by andref on 12/05/16.
*/
class Database {
val url = "jdbc:mysql://localhost/playdb"
val db = Database.forURL(url, driver = "com.mysql.jdbc.Driver")
override def finalize() {
db.close()
super.finalize()
}
}
cat EmailMessageTable.scala
package com.github.odnanref.EmailFilter
import java.sql.Timestamp
import slick.driver.JdbcProfile
import slick.driver.MySQLDriver.api._
import scala.concurrent.Future
class EmailMessageTable(tag: Tag) extends Table[EmailMessage](tag, "email_message") {
def id = column[Option[Long]]("id", O.AutoInc, O.PrimaryKey)
def email = column[String]("email")
def subject = column[String]("subject")
def body = column[String]("body")
def datain = column[Timestamp]("datain")
def email_id= column[Long]("email_id")
def * = (id, email, subject, body, datain, email_id) <> ((EmailMessage.apply _).tupled, EmailMessage.unapply)
def ? = (id.get.?, email.?, subject.?, body.?, datain.?).shaped.<>({ r =>; _1.map(_ =>
EmailMessage.tupled((_1, _2.get, _3.get, _4.get, _5.get))) }, (_: Any) =>
throw new Exception("Inserting into ? projection not supported."))
}
I can't initialize the database and execute search query's or insert statements based on this code I try to do
val db = new Database()
db.db.run(TableQuery[EmailMessageTable] += EmailMessage(...) )
And it says, it doesn't know the method +=
Also I get this error:
Database.scala:4: imported `Database' is permanently hidden by definition of class Database in package EmailFilter
[warn] import slick.driver.MySQLDriver.backend.Database
What am I doing wrong?
Post EDIT>
package com.github.odnanref.EmailFilter
import java.sql.Timestamp
case class EmailMessage(
id: Option[Long],
email: String,
subject:String,
body:String,
datain: Timestamp,
email_id: Long
)
You are importing a class named Database inside a file that defines another class with the same name. You can:
rename your Database class:
class MyDatabase {
val url = ...
val db = ...
...
}
rename imported class:
import slick.driver.MySQLDriver.backend.{Database => SlickDB}
...
val db = SlickDB.forURL(url, driver = "com.mysql.jdbc.Driver")
avoid importing Database explicitly:
import slick.driver.MySQLDriver.backend
...
val db = backend.Database.forURL(url, driver = "com.mysql.jdbc.Driver")

Scala Slick - Infinite recursion (StackOverflowError) on query results

I am playing around with Slick on top of a Twitter Finatra application. Finally I thought I made it but now, when I want to process a result, I always get an recursion error. I looked around but I did not find anything helpful for this particular problem. The code I have is actually quite simple:
Map the Database class to a custom Type:
package com.configurationManagement.library
package object Types {
type SlickDatabase = slick.driver.MySQLDriver.api.Database
}
Model:
package com.configurationManagement.app.domain
import slick.lifted.Tag
import slick.driver.MySQLDriver.api._
import slick.profile.SqlProfile.ColumnOption.NotNull
case class ConfigurationTemplate(id: Option[Int], name: String)
class ConfigurationTemplates(tag: Tag) extends Table[ConfigurationTemplate](tag: Tag, "configuration_template") {
def id = column[Int]("id", O.AutoInc, O.PrimaryKey)
def name = column[String]("name", NotNull)
def uniqueNameIndex = index("unique_name", name, unique = true)
def * = (id.?, name) <> (ConfigurationTemplate.tupled, ConfigurationTemplate.unapply)
}
Controller:
package com.configurationManagement.app.controller
import com.google.inject.{Inject, Singleton}
import com.configurationManagement.app.domain.ConfigurationTemplates
import com.configurationManagement.app.dto.request.RequestConfigurationTemplateDto
import com.configurationManagement.library.Types._
import com.twitter.finatra.http.Controller
import com.twitter.inject.Logging
import slick.driver.MySQLDriver.api._
#Singleton
class ConfigurationTemplateController #Inject()(database: SlickDatabase)
extends Controller with Logging with FutureConverter {
post("/configurations/templates") { dto: RequestConfigurationTemplateDto =>
val templates = TableQuery[ConfigurationTemplates]
val query = templates.filter(_.id === 1)
response.ok(query.map(_.name))
}
}
And here comes the error
Infinite recursion (StackOverflowError) (through reference chain: com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast.TableNode["driver_table"]->com.configurationManagement.app.domain.ConfigurationTemplates["table_node"]->slick.ast
Obvisously this line causes the error:
query.map(_.name)
Two things I see, first you need to add .result to the query to transform it into a FixedSqlStreamingAction, second you need a database to run that query on:
private[this] val database: slick.driver.MySQLDriver.backend.DatabaseDef = Database.forDataSource(...)
database.run(templates.filter(_.id === 1).map(_.name).result)
Which returns a Future[Seq[String]], this probably is the expected type from response.ok.

How to omit column values when doing a bulk-insert slick 3.x?

I have a JOURNAL table where the INSERT_DATE column should be filled by the DB with the current date and time when the record is inserted. I did not use the TIMESTAMP type on purpose, because of its limited range.
class Journal(tag: Tag) extends Table[JournalEntry](tag, "JOURNAL") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def insertDate = column[OffsetDateTime]("INSERT_DATE", SqlType("DateTime default CURRENT_TIMESTAMP"))(localDateTimeColumnType)
def valueDate = column[OffsetDateTime]("VALUE_DATE", SqlType("DateTime"))(localDateTimeColumnType)
def amount = column[Int]("AMOUNT")
def note = column[String]("NOTE", O.Length(100))
def * : ProvenShape[JournalEntry] = (id.?, insertDate.?, valueDate, amount, note)
<> ((JournalEntry.apply _).tupled, JournalEntry.unapply)
}
I also implement a case class:
case class JournalEntry(id: Option[Int], insertDate: Option[LocalDateTime],
valueDate: LocalDateTime, amount: Int, note: String)
When my app starts up, I populate the DB with random test data:
TableQuery[Journal] ++= Seq.fill(1000)(JournalEntry(None, Some(LocalDateTime.now()),
LocalDateTime.of(2006 + Random.nextInt(10), 1 + Random.nextInt(11),
1 + Random.nextInt(27),Random.nextInt(24), Random.nextInt(60)), Random.nextInt(),
TestDatabase.randomString(100)))
This works, but the INSERT_DATE ist set by the JVM not by the Database. The Slick docs say that columns should be omitted, if one wants the default value to get inserted. But I just dont get how I omit columns if I have a case class.
I also found this SO post but could not figure out how to use it in my context.
Any ideas?
The Slick docs give an example of such omission right in the first code snippet here. Follow the steps or the cvogt's answer and you will arrive at the solution:
TableQuery[Journal].map(je => (je.id, je.valueDate, je.amount, je.note)) ++= Seq.fill(1000)((None, LocalDateTime.of(2006 + Random.nextInt(10), 1 + Random.nextInt(11), 1 + Random.nextInt(27),Random.nextInt(24), Random.nextInt(60)), Random.nextInt(), TestDatabase.randomString(100)))
I work in the following way:
import java.time.{ ZonedDateTime, ZoneOffset}
import slick.profile.SqlProfile.ColumnOption.SqlType
import scala.concurrent.duration.Duration
import scala.concurrent.Await
implicit val zonedDateTimeType = MappedColumnType.base[ZonedDateTime, Timestamp](
{dt =>Timestamp.from(dt.toInstant)},
{ts =>ZonedDateTime.ofInstant(ts.toInstant, ZoneOffset.UTC)}
)
class Users(tag: Tag) extends Table[(String, ZonedDateTime)](tag, "users") {
def name = column[String]("name")
def createAt = column[ZonedDateTime]("create_at", SqlType("timestamp not null default CURRENT_TIMESTAMP"))
def * = (name, createAt)
}
val users = TableQuery[Users]
val setup = DBIO.seq(
users.schema.create,
users.map(u => (u.name)) ++= Seq(("Amy"), ("Bob"), ("Chris"), ("Dave"))
Await.result(db.run(setup), Duration.Inf)
I am not using case class here, just a tuple.

play-slick : not found table

I want to use play-slick 1.0.0 with play 2.4.0.
According to the sample (https://github.com/playframework/play-slick/tree/master/samples/basic), I defined the UserTable like this:
package tables
import models.User
import scala.slick.driver.JdbcProfile
trait UserTable {
protected val driver: JdbcProfile
import driver.api._
class Users(tag: Tag) extends Table[User](tag, "users"){
def ID = column[Long]("id", O.PrimaryKey, O.AutoInc)
def email = column[String]("email", O.NotNull)
def password = column[String]("password", O.NotNull)
def * = (ID, email, password) <> (User.tupled, User.unapply)
}
}
And I implemented the controller as follow:
package controllers
import play.api._
import play.api.mvc._
import play.api.db.slick.DatabaseConfigProvider
import play.api.db.slick.HasDatabaseConfig
import tables.UserTable
import scala.slick.driver.JdbcProfile
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import models.User
import tables.UserTable
class UserController extends Controller with UserTable with HasDatabaseConfig[JdbcProfile]{
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](Play.current)
import driver.api._
val Users = TableQuery[Users]
def index = Action.async {
db.run(Users.result).map(res => Ok(views.html.User.index(res.toList)))
}
}
But, I ran the application and invoked this controller, I got the error
[SQLException: [SQLITE_ERROR] SQL error or missing database (no such table: users)]
How can I create the "users" table?
You need to either use an existing users table or create a table through slick.
Please see http://slick.typesafe.com/doc/3.0.0/gettingstarted.html#populating-the-database
You need to run db.run(Users.schema.create) in your application.

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)