Reputation: 21
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]
}
Upvotes: 1
Views: 917
Reputation: 21
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))
}
Upvotes: 1