I am trying to implement a simple Repository in a PlayFramework app with the play-slick plugin.
I am new to scala, new to slick, new to play.
When trying to compile my code, i get the following error:
type mismatch;
found : slick.lifted.TableQuery[UserRepository.this.UserTable]
required: UserRepository.this.profile.api.TableQuery[UserRepository.this.BaseTable]
(which expands to) slick.lifted.TableQuery[UserRepository.this.BaseTable]
Note: UserRepository.this.UserTable <: UserRepository.this.BaseTable, but class TableQuery is invariant in type E.
You may wish to define E as +E instead. (SLS 4.5)
Here is my code:
trait BaseEntity {
def id: Long
def createdAt: LocalDateTime
def updatedAt: LocalDateTime
def deletedAt: LocalDateTime
}
case class User (id: Long, firstname: String, lastname: String, password: String, createdAt: LocalDateTime, updatedAt: LocalDateTime, deletedAt: LocalDateTime) extends BaseEntity
abstract class BaseRepository[E <: BaseEntity](protected val dbConfigProvider: DatabaseConfigProvider, executionContext: ExecutionContext) extends HasDatabaseConfigProvider[JdbcProfile] {
import profile.api._
implicit val localDateTimeToTimestamp: BaseColumnType[LocalDateTime] = MappedColumnType.base[LocalDateTime, Timestamp](
l => Timestamp.valueOf(l),
t => t.toLocalDateTime
)
protected abstract class BaseTable(tag: Tag, tableName: String) extends Table[E](tag, tableName) {
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def createdAt = column[LocalDateTime]("created_at")
def updatedAt = column[LocalDateTime]("updated_at")
def deletedAt = column[LocalDateTime]("deleted_at")
}
protected val query: TableQuery[BaseTable]
def all(): Future[Seq[E]] = db.run(query.result)
}
class UserRepository #Inject()(#NamedDatabase("effective-potato-play") override protected val dbConfigProvider: DatabaseConfigProvider)(implicit executionContext: ExecutionContext) extends BaseRepository[User](dbConfigProvider, executionContext) {
import profile.api._
protected class UserTable(tag: Tag) extends BaseTable(tag, "user") {
def firstname = column[String]("firstname")
def lastname = column[String]("lastname")
def password = column[String]("password")
def * = (id, firstname, lastname, password, createdAt, updatedAt, deletedAt) <> (User.tupled, User.unapply)
}
protected val query = TableQuery[UserTable]
}
pamus comment led me in the right directions.
I solved the following by moving the inner Table-Class to an own class and giving the query for the repository as constructor argument.
abstract class BaseRepository[E <: BaseEntity, T <: BaseTable[E]](val dbConfigProvider: DatabaseConfigProvider, executionContext: ExecutionContext, query: TableQuery[T]) extends HasDatabaseConfigProvider[JdbcProfile] {
def all(): Future[Seq[E]] = db.run(query.result)
def find(id: Long): Future[Option[E]] = db.run(query.filter(_.id === id).result.headOption)
}
class UserRepository #Inject()(dbConfigProvider: DatabaseConfigProvider)(implicit executionContext: ExecutionContext) extends BaseRepository[User, UserTable](dbConfigProvider, executionContext, TableQuery[UserTable]) {
}
abstract class BaseTable[E <: BaseEntity](tag: Tag, tableName: String) extends Table[E](tag, tableName) {
implicit val localDateTimeToTimestamp: BaseColumnType[LocalDateTime] = MappedColumnType.base[LocalDateTime, Timestamp](
l => Timestamp.valueOf(l),
t => t.toLocalDateTime
)
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def createdAt = column[LocalDateTime]("created_at")
def updatedAt = column[LocalDateTime]("updated_at")
def deletedAt = column[Option[LocalDateTime]]("deleted_at", O.Default(null))
}
class UserTable(tag: Tag) extends BaseTable[User](tag, "user") {
def firstname = column[String]("firstname")
def lastname = column[String]("lastname")
def password = column[String]("password")
def * = (id, firstname, lastname, password, createdAt, updatedAt, deletedAt) <> (User.tupled, User.unapply)
}
I can then use the queries from the BaseRepository like so
userRepository.find(1).map { user =>
Ok(Json.toJson(user))
}
Related
trying work with Sangria and Slick. New to both of them.
I have bunch of tables which share a list of common fields. Slick's representation of this is below:
case class CommonFields(created_by: Int = 0, is_deleted: Boolean = false)
trait CommonModel {
def commonFields: CommonFields
def created_by = commonFields.created_by
def is_deleted = commonFields.is_deleted
}
case class User(id: Int,
name: String,
commonFields: CommonFields = CommonFields()) extends CommonModel
Slick tables:
abstract class CommonTable [Model <: CommonModel] (tag: Tag, tableName: String) extends Table[Model](tag, tableName) {
def created_by = column[Int]("created_by")
def is_deleted = column[Boolean]("is_deleted")
}
case class CommonColumns(created_by: Rep[Int], is_deleted: Rep[Boolean])
implicit object CommonShape extends CaseClassShape(
CommonColumns.tupled, CommonFields.tupled
)
class UsersTable(tag: Tag) extends CommonTable[User](tag, "USERS") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def name = column[String]("NAME")
def * = (id,
name,
CommonColumns(created_by, is_deleted)) <> (User.tupled, User.unapply)
}
val Users = TableQuery[UsersTable]
The problem is with Graphql:
lazy val UserType: ObjectType[Unit, User] = deriveObjectType[Unit, User]()
When I try to create UserType using derivedObjectType Macro, it complains that
Can't find suitable GraphQL output type for .CommonFields. If you have defined it already, please consider making it implicit and ensure that it's available in the scope.
[error] lazy val UserType: ObjectType[Unit, User] = deriveObjectType[Unit, User](
How do I tell Sangria/Graphql how to handle this nested list of fields (from CommonFields) ?
Please help.
You are deriving the Type for User but user also has CommonFields which you have not derived. So it's not able to find Type information for CommonFields. Derive both and make the derivation implicit for CommonFields.
implicit val CommonFieldsType: ObjectType[Unit, CommonFields] = deriveObjectType[Unit, CommonFields]
implicit val UserType: ObjectType[Unit, User] = deriveObjectType[Unit, User]
I have currently this code:
class UsersRepository #Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
private val Users = TableQuery[UsersTable]
def findByName(name: String): Future[Option[User]] =
db.run(Users.filter(_.name === name).take(1).result.headOption)
implicit val localDateTimeColumnType = MappedColumnType.base[LocalDateTime, Timestamp](
d => Timestamp.from(d.toInstant(ZoneOffset.ofHours(0))),
d => d.toLocalDateTime
)
trait GenericTable {
this: Table[_] =>
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def createdAt = column[LocalDateTime]("created_at")
def updatedAt = column[LocalDateTime]("updated_at")
}
private class UsersTable(tag: Tag) extends Table[User](tag, "User") with GenericTable {
def name = column[String]("name")
def url = column[String]("url")
def nameIndex = index("name_index", name, unique = true)
override def * = (id, createdAt, updatedAt, name, url) <> (User.tupled, User.unapply)
}
}
How can I now easily move the GenericTable trait out of the UsersRepository so that I can use it with other tables? If I just move it out, then things like column, Table and O are not found anymore, because I loose the things from the driver import.
I also would like to move the UsersTable definition itself out of the DAO/repository class. I have the same problem in this case.
Thanks,
Magnus
Provider dbConfig in the trait as a no input parameter def
trait GenericTableProvider {
val dbConfig: DatabaseConfig[JdbcProfile]
import dbConfig.driver.api._
trait GenericTable {
this: Table[_] =>
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def createdAt = column[LocalDateTime]("created_at")
def updatedAt = column[LocalDateTime]("updated_at")
}
}
and then use it as shown in the code snippet below.
class UsersRepository #Inject()(protected override val dbConfigProvider: DatabaseConfigProvider)
extends HasDatabaseConfigProvider[JdbcProfile]
with GenericTableProvider {
import driver.api._
private val Users = TableQuery[UsersTable]
private class UsersTable(tag: Tag) extends Table[User](tag, "User") with GenericTable {
def name = column[String]("name")
def url = column[String]("url")
def nameIndex = index("name_index", name, unique = true)
override def * = (id, createdAt, updatedAt, name, url) <> (User.tupled, User.unapply)
}
.....
}
override def dbConfigProvider with a val dbConfigProvider
I'm scala/play/slick newbie so please don't be too mad if I ask dumb question.
Here goes the question.
I have several slick table definitions, here is one of them:
import javax.inject.Inject
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.db.NamedDatabase
import slick.driver.JdbcProfile
import scala.concurrent.Future
case class User(id: Int, login: String, password: String) extends Identifiable
class UserDAO #Inject()(#NamedDatabase protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
private val users = TableQuery[UsersTable]
def all(): Future[Seq[User]] = db.run(users.result)
def insert(dog: User): Future[Unit] = db.run(users += dog).map { _ => () }
def delete(id: Int): Future[Int] = db.run(users.filter(_.id === id).delete)
private class UsersTable(tag: Tag) extends Table[User](tag, "USER") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def email = column[String]("email")
def password = column[String]("password")
def * = (id, email, password) <> (User.tupled, User.unapply)
}
}
Imagine I have much more tables which have def id = column[Int]("id", O.PrimaryKey, O.AutoInc) to eliminate this I need to write something like:
trait Identifiable {
this: Table[_] =>
def id = column[String]("id", O.PrimaryKey)
}
But how do I import Table here in a database agnostic manner? Moreover there is more room for enhancements: all DAO objects providing access to Identifiable Tables can be inherited from a common abstract class containing all, insert, find and delete methods. Something like (was unable to compile it):
abstract class BaseDAO[E <: Identifiable] extends DAO[E] with HasDatabaseConfigProvider[JdbcProfile] {
import driver.api._
private val entities = TableQuery[BaseTable]
def all(): Future[Seq[E]] = db.run(entities.result)
def insert(entity: E): Future[Unit] = db.run(entities += entity).map { _ => () }
def delete(entity: E): Future[Int] = db.run(entities.filter(_.id === entity.id).delete)
def find(id: Int): Future[E] = db.run(entities.filter(_.id === entities.id))
trait BaseTable { this: Table[_] =>
def id = column[String]("id", O.PrimaryKey, O.AutoInc)
}
}
Could somebody please point me to my mistakes? Thanks.
Database agnostic and Code is highly reusable
I am using Slick with Playframework and this is how I achieved database agnostic and generic repository.
Note that this work is inspired from Active Slick
I want to have basic crud operations like this to be defined on my case class. I should be able to do count, update, delete and create. I want to write the curd code just once and reuse it for ever.
Here is the snippet which demonstrates this.
case class Dog(name: String, id: Option[Long] = None)
Dog("some_dog").save()
Dog("some_dog").insert()
Dog("some_dog", Some(1)).delete()
CrudActions.scala
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile
import scala.concurrent.ExecutionContext
trait CrudActions {
val dbConfig: DatabaseConfig[JdbcProfile]
import dbConfig.driver.api._
type Model
def count: DBIO[Int]
def save(model: Model)(implicit ec: ExecutionContext): DBIO[Model]
def update(model: Model)(implicit ec: ExecutionContext): DBIO[Model]
def delete(model: Model)(implicit ec: ExecutionContext): DBIO[Int]
def fetchAll(fetchSize: Int = 100)(implicit ec: ExecutionContext): StreamingDBIO[Seq[Model], Model]
}
Now lets get our Entity into picture. Note that Entity is nothing but our case class
Entity is case class on which we do crud operations. For locating our entity lets also have Id in place. Id is important for locating and operating an entity or record in the database. Also Id uniquely identities for entity
EntityActionsLike.scala
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile
import scala.concurrent.ExecutionContext
trait EntityActionsLike extends CrudActions {
val dbConfig: DatabaseConfig[JdbcProfile]
import dbConfig.driver.api._
type Entity
type Id
type Model = Entity
def insert(entity: Entity)(implicit ec: ExecutionContext): DBIO[Id]
def deleteById(id: Id)(implicit ec: ExecutionContext): DBIO[Int]
def findById(id: Id)(implicit ec: ExecutionContext): DBIO[Entity]
def findOptionById(id: Id)(implicit ec: ExecutionContext): DBIO[Option[Entity]]
}
import slick.ast.BaseTypedType
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile
import scala.concurrent.ExecutionContext
Now lets implement these methods. For doing operations we need Table and TableQuery. Lets say we have table and tableQuery. The good about traits is we can declare a contract and leave the implementation details to subclasses or subtypes
EntityActions.scala
trait EntityActions extends EntityActionsLike {
val dbConfig: DatabaseConfig[JdbcProfile]
import dbConfig.driver.api._
type EntityTable <: Table[Entity]
def tableQuery: TableQuery[EntityTable]
def $id(table: EntityTable): Rep[Id]
def modelIdContract: ModelIdContract[Entity,Id]
override def count: DBIO[Int] = tableQuery.size.result
override def insert(entity: Entity)(implicit ec: ExecutionContext): DBIO[Id] = {
tableQuery.returning(tableQuery.map($id(_))) += entity
}
override def deleteById(id: Id)(implicit ec: ExecutionContext): DBIO[Int] = {
filterById(id).delete
}
override def findById(id: Id)(implicit ec: ExecutionContext): DBIO[Entity] = {
filterById(id).result.head
}
override def findOptionById(id: Id)(implicit ec: ExecutionContext): DBIO[Option[Entity]] = {
filterById(id).result.headOption
}
override def save(model: Entity)(implicit ec: ExecutionContext): DBIO[Entity] = {
insert(model).flatMap { id =>
filterById(id).result.head
}.transactionally
}
override def update(model: Entity)(implicit ec: ExecutionContext): DBIO[Entity] = {
filterById(modelIdContract.get(model)).update(model).map { _ => model }.transactionally
}
override def delete(model: Entity)(implicit ec: ExecutionContext): DBIO[Int] = {
filterById(modelIdContract.get(model)).delete
}
override def fetchAll(fetchSize: Int)(implicit ec: ExecutionContext): StreamingDBIO[Seq[Entity], Entity] = {
tableQuery.result.transactionally.withStatementParameters(fetchSize = fetchSize)
}
def filterById(id: Id) = tableQuery.filter($id(_) === id)
def baseTypedType: BaseTypedType[Id]
protected implicit lazy val btt: BaseTypedType[Id] = baseTypedType
}
ActiveRecord.scala
import slick.dbio.DBIO
import scala.concurrent.ExecutionContext
abstract class ActiveRecord[R <: CrudActions](val repo: R) {
def model: repo.Model
def save()(implicit ec: ExecutionContext): DBIO[repo.Model] = repo.save(model)
def update()(implicit ec: ExecutionContext): DBIO[repo.Model] = repo.update(model)
def delete()(implicit ec: ExecutionContext): DBIO[Int] = repo.delete(model)
}
ModelContract.scala
case class ModelIdContract[A, B](get: A => B, set: (A, B) => A)
How to Use
Sample.scala
import com.google.inject.{Inject, Singleton}
import play.api.db.slick.DatabaseConfigProvider
import slick.ast.BaseTypedType
import slick.backend.DatabaseConfig
import slick.driver.JdbcProfile
import slick.{ActiveRecord, EntityActions, ModelIdContract}
case class Dog(name: String, id: Option[Long] = None)
#Singleton
class DogActiveRecord #Inject() (databaseConfigProvider: DatabaseConfigProvider) extends EntityActions {
override val dbConfig: DatabaseConfig[JdbcProfile] = databaseConfigProvider.get[JdbcProfile]
import dbConfig.driver.api._
override def tableQuery = TableQuery(new Dogs(_))
override def $id(table: Dogs): Rep[Id] = table.id
override def modelIdContract: ModelIdContract[Dog, Id] = ModelIdContract(dog => dog.id.get, (dog, id) => dog.copy(id = Some(id)))
override def baseTypedType: BaseTypedType[Id] = implicitly[BaseTypedType[Id]]
override type Entity = Dog
override type Id = Long
override type EntityTable = Dogs
class Dogs(tag: Tag) extends Table[Dog](tag, "DogsTable") {
def name = column[String]("name")
def id = column[Long]("id", O.PrimaryKey)
def * = (name, id.?) <> (Dog.tupled, Dog.unapply)
}
implicit class ActiveRecordImplicit(val model: Entity) extends ActiveRecord(this)
import scala.concurrent.ExecutionContext.Implicits.global
val result = Dog("some_dog").save()
val res2 = Dog("some_other_dog", Some(1)).delete()
val res3 = Dog("some_crazy_dog", Some(1)).update()
}
Now we can do operations on Dog directly like this
Dog("some_dog").save()
This implicit does the magic for us
implicit class ActiveRecordImplicit(val model: Entity) extends ActiveRecord(this)
You can also add scheme creation and dropping logic in EntityActions
tableQuery.schema.create
table.schema.drop
Have such models (simplified):
case class User(id:Int,name:String)
case class Address(id:Int,name:String)
...
Slick (2.1.0 version) table mapping:
class Users(_tableTag: Tag) extends Table[User](_tableTag, "users") with WithId[Users, User] {`
val id: Column[Int] = column[Int]("id", O.AutoInc, O.PrimaryKey)
...
}
trait WithId[T, R] {
this: Table[R] =>
def id: Column[Int]
}
Mixing trait WithId I want to implement generic DAO methods for different tables with column id: Column[Int] (I want method findById to work with both User and Address table mappings)
trait GenericSlickDAO[T <: WithId[T, R], R] {
def db: Database
def findById(id: Int)(implicit stk: SlickTableQuery[T]): Option[R] = db.withSession { implicit session =>
stk.tableQuery.filter(_.id === id).list.headOption
}
trait SlickTableQuery[T] {
def tableQuery: TableQuery[T]
}
object SlickTableQuery {
implicit val usersQ = new SlickTableQuery[Users] {
val tableQuery: Table Query[Users] = Users
}
}
The problem is that findById doesn't compile:
Error:(13, 45) type mismatch;
found : Option[T#TableElementType] required: Option[R]
stk.tableQuery.filter(_.id === id).list.headOption
As I see it T is of type WithId[T, R] and at the same time is of type Table[R]. Slick implements the Table type such that if X=Table[Y] then X#TableElementType=Y.
So in my case T#TableElementType=R and Option[T#TableElementType] should be inferred as Option[R] but it isn't. Where am I wrong?
Your assumption about WithId[T, R] being of type Table[R] is wrong. The self-type annotation in WithId[T, R] just requires a Table[R] to be mixed in, but that doesn't mean that WithId[T, R] is a Table[R].
I think you confuse the declaration of WithId with instances of WithId which eventually need to be an instance of a Table.
Your upper type bound constraint in the GenericSlickDAO trait also doesn't guarantee you the property of WithId to be an instance of Table, since any type is a subtype of itself.
See this question for a more elaborate explanation about the differences between self-types and subtypes.
I'm using play-slick and I tried to do exactly like you, with a trait and using self-type without success.
But I succeeded with the following:
import modelsunscanned.TableWithId
import scala.slick.jdbc.JdbcBackend
import scala.slick.lifted.TableQuery
import play.api.db.slick.Config.driver.simple._
/**
* #author Sebastien Lorber (lorber.sebastien#gmail.com)
*/
package object models {
private[models] val Users = TableQuery(new UserTable(_))
private[models] val Profiles = TableQuery(new ProfileTable(_))
private[models] val Companies = TableQuery(new CompanyTable(_))
private[models] val Contacts = TableQuery(new ContactTable(_))
trait ModelWithId {
val id: String
}
trait BaseRepository[T <: ModelWithId] {
def tableQuery: TableQuery[TableWithId[T]]
private val FindByIdQuery = Compiled { id: Column[String] =>
tableQuery.filter(_.id === id)
}
def insert(t: T)(implicit session: JdbcBackend#Session) = {
tableQuery.insert(t)
}
def getById(id: String)(implicit session: JdbcBackend#Session): T = FindByIdQuery(id).run.headOption
.getOrElse(throw new RuntimeException(s"Could not find entity with id=$id"))
def findById(id: String)(implicit session: JdbcBackend#Session): Option[T] = FindByIdQuery(id).run.headOption
def update(t: T)(implicit session: JdbcBackend#Session): Unit = {
val nbUpdated = tableQuery.filter(_.id === t.id).update(t)
require(nbUpdated == 1,s"Exactly one should have been updated, not $nbUpdated")
}
def delete(t: T)(implicit session: JdbcBackend#Session) = {
val nbDeleted = tableQuery.filter(_.id === t.id).delete
require(nbDeleted == 1,s"Exactly one should have been deleted, not $nbDeleted")
}
def getAll(implicit session: JdbcBackend#Session): List[T] = tableQuery.list
}
}
// play-slick bug, see https://github.com/playframework/play-slick/issues/227
package modelsunscanned {
abstract class TableWithId[T](tableTag: Tag,tableName: String) extends Table[T](tableTag,tableName) {
def id: Column[String]
}
}
I give you an exemple usage:
object CompanyRepository extends BaseRepository[Company] {
// Don't know yet how to avoid that cast :(
def tableQuery = Companies.asInstanceOf[TableQuery[TableWithId[Company]]]
// Other methods here
...
}
case class Company(
id: String = java.util.UUID.randomUUID().toString,
name: String,
mainContactId: String,
logoUrl: Option[String],
activityDescription: Option[String],
context: Option[String],
employeesCount: Option[Int]
) extends ModelWithId
class CompanyTable(tag: Tag) extends TableWithId[Company](tag,"COMPANY") {
override def id = column[String]("id", O.PrimaryKey)
def name = column[String]("name", O.NotNull)
def mainContactId = column[String]("main_contact_id", O.NotNull)
def logoUrl = column[Option[String]]("logo_url", O.Nullable)
def activityDescription = column[Option[String]]("description", O.Nullable)
def context = column[Option[String]]("context", O.Nullable)
def employeesCount = column[Option[Int]]("employees_count", O.Nullable)
//
def * = (id, name, mainContactId,logoUrl, activityDescription, context, employeesCount) <> (Company.tupled,Company.unapply)
//
def name_index = index("idx_name", name, unique = true)
}
Note that active-slick is also using something similar
This helped me out a lot. It's a pretty simple example of a genericdao https://gist.github.com/lshoo/9785645
package slicks.docs.dao
import scala.slick.driver.PostgresDriver.simple._
import scala.slick.driver._
trait Profile {
val profile: JdbcProfile
}
trait CrudComponent {
this: Profile =>
abstract class Crud[T <: Table[E] with IdentifiableTable[PK], E <: Entity[PK], PK: BaseColumnType](implicit session: Session) {
val query: TableQuery[T]
def count: Int = {
query.length.run
}
def findAll: List[E] = {
query.list()
}
def queryById(id: PK) = query.filter(_.id === id)
def findOne(id: PK): Option[E] = queryById(id).firstOption
def add(m: E): PK = (query returning query.map(_.id)) += m
def withId(model: E, id: PK): E
def extractId(m: E): Option[PK] = m.id
def save(m: E): E = extractId(m) match {
case Some(id) => {
queryById(id).update(m)
m
}
case None => withId(m, add(m))
}
def saveAll(ms: E*): Option[Int] = query ++= ms
def deleteById(id: PK): Int = queryById(id).delete
def delete(m: E): Int = extractId(m) match {
case Some(id) => deleteById(id)
case None => 0
}
}
}
trait Entity[PK] {
def id: Option[PK]
}
trait IdentifiableTable[I] {
def id: Column[I]
}
package slicks.docs
import slicks.docs.dao.{Entity, IdentifiableTable, CrudComponent, Profile}
case class User(id: Option[Long], first: String, last: String) extends Entity[Long]
trait UserComponent extends CrudComponent {
this: Profile =>
import profile.simple._
class UsersTable(tag: Tag) extends Table[User](tag, "users") with IdentifiableTable[Long] {
override def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
def first = column[String]("first")
def last = column[String]("last")
def * = (id.?, first, last) <> (User.tupled, User.unapply)
}
class UserRepository(implicit session: Session) extends Crud[UsersTable, User, Long] {
override def query = TableQuery[UsersTable]
override def withId(user: User, id: Long): User = user.copy(id = Option(id))
}
}
I use slick 2.0 rc
I have defined field UserId:
trait TypedId extends Any {
def value: Long
}
trait HasId[ID <: TypedId] {
_: Table[_] =>
def id: Column[ID]
}
case class UserId(value:Long) extends AnyVal with TypedId
case class User(id: Option[UserId],
email: String,
firstName: String,
lastName: String,
phone:String)
when I use it:
class Users(tag: Tag) extends Table[User](tag, "users") with HasId[Option[UserId]] {
def * = (id.?, email, firstName , lastName , phone )<> (User.tupled, User.unapply)
def id= column[Option[UserId]]("ID", O.AutoInc, O.PrimaryKey)
def email = column[String]("EMAIL", O.NotNull)
def firstName = column[String]("FIRST_NAME", O.NotNull)
def lastName = column[String]("LAST_NAME", O.NotNull)
def phone =column[String]("PHONE", O.NotNull)
}
it give me compile error as:
[error] C:\assigment\slick-advanced\app\models\User.scala:27: could not find imp
licit value for parameter tm: scala.slick.ast.TypedType[Option[models.UserId]]
[error] def id= column[Option[UserId]]("ID", O.AutoInc, O.PrimaryKey)
This seems to be a duplicate of the already answered question, which can be found here: on slick 2.0, I find I can not store user defined field