9

I'm struggling with Slick's lifted embedding and mapped tables. The API feels strange to me, maybe just because it is structured in a way that's unfamiliar to me.

I want to build a Task/Todo-List. There are two entities:

  • Task: Each task has a an optional reference to the next task. That way a linked list is build. The intention is that the user can order the tasks by his priority. This order is represented by the references from task to task.
  • TaskList: Represents a TaskList with a label and a reference to the first Task of the list.

    case class Task(id: Option[Long], title: String, nextTask: Option[Task])
    case class TaskList(label: String, firstTask: Option[Task])


Now I tried to write a data access object (DAO) for these two entities.

import scala.slick.driver.H2Driver.simple._
import slick.lifted.MappedTypeMapper

implicit val session: Session = Database.threadLocalSession
val queryById = Tasks.createFinderBy( t => t.id )

def task(id: Long): Option[Task] = queryById(id).firstOption

  private object Tasks extends Table[Task]("TASKS") {
    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
    def title = column[String]("TITLE")
    def nextTaskId = column[Option[Long]]("NEXT_TASK_ID")
    def nextTask = foreignKey("NEXT_TASK_FK", nextTaskId, Tasks)(_.id)
    def * = id ~ title ~ nextTask <> (Task, Task.unapply _)
  }

  private object TaskLists extends Table[TaskList]("TASKLISTS") {
    def label = column[String]("LABEL", O.PrimaryKey)
    def firstTaskId = column[Option[Long]]("FIRST_TASK_ID")
    def firstTask = foreignKey("FIRST_TASK_FK", firstTaskId, Tasks)(_.id)
    def * = label ~ firstTask <> (Task, Task.unapply _)
  }

Unfortunately it does not compile. The problems are in the * projection of both tables at nextTask respective firstTask.

  • could not find implicit value for evidence parameter of type scala.slick.lifted.TypeMapper[scala.slick.lifted.ForeignKeyQuery[SlickTaskRepository.this.Tasks.type,justf0rfun.bookmark.model.Task]]
  • could not find implicit value for evidence parameter of type scala.slick.lifted.TypeMapper[scala.slick.lifted.ForeignKeyQuery[SlickTaskRepository.this.Tasks.type,justf0rfun.bookmark.model.Task]]

I tried to solve that with the following TypeMapper but that does not compile, too.

  implicit val taskMapper = MappedTypeMapper.base[Option[Long], Option[Task]](
    option => option match {
      case Some(id) => task(id)
      case _ => None
    },
    option => option match {
      case Some(task) => task.id
      case _ => None
    })
  • could not find implicit value for parameter tm: scala.slick.lifted.TypeMapper[Option[justf0rfun.bookmark.model.Task]]
  • not enough arguments for method base: (implicit tm: scala.slick.lifted.TypeMapper[Option[justf0rfun.bookmark.model.Task]])scala.slick.lifted.BaseTypeMapper[Option[Long]]. Unspecified value parameter tm.

Main question: How to use Slick's lifted embedding and mapped tables the right way? How to I get this to work?

Thanks in advance.

user573215
  • 4,679
  • 5
  • 22
  • 25

1 Answers1

6

The short answer is: Use ids instead of object references and use Slick queries to dereference ids. You can put the queries into methods for re-use.

That would make your case classes look like this:

case class Task(id: Option[Long], title: String, nextTaskId: Option[Long])
case class TaskList(label: String, firstTaskId: Option[Long])

I'll publish an article about this topic at some point and link it here.

cvogt
  • 11,260
  • 30
  • 46