Jack
Jack

Reputation: 16718

Trouble updating a record with Slick

With a class and table definition looking like this:

case class Group(
  id: Long = -1,
  id_parent: Long = -1,
  label: String = "",
  description: String = "")

  object Groups extends Table[Group]("GROUPS") {
    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
    def id_parent = column[Long]("ID_PARENT")
    def label = column[String]("LABEL")
    def description = column[String]("DESC")
    def * = id ~ id_parent ~ label ~ design <> (Group, Group.unapply _)
    def autoInc = id_parent ~ label ~ design returning id into {
      case ((_, _, _), id) => id
    }
  }

To update a record, I can do this:

  def updateGroup(id: Long) = Groups.where(_.id === id)

  def updateGroup(g: Group)(implicit session: Session) = updateGroup(g.id).update(g)

But I can't get updates to work using for expressions:

  val findGById = for {
    id <- Parameters[Long]
    g <- Groups; if g.id === id
  } yield g

  def updateGroupX(g: Group)(implicit session: Session) = findGById(g.id).update(g)
  ----------------------------------------------------------------------------^
Error: value update is not a member of scala.slick.jdbc.MutatingUnitInvoker[com.exp.Group]

I'm obviously missing something in the documentation.

Upvotes: 6

Views: 5052

Answers (2)

Mike G.
Mike G.

Reputation: 690

Refer to http://madnessoftechnology.blogspot.ru/2013/01/database-record-updates-with-slick-in.html

I stuck with the updating today, and this blog post helped me much. Also refer to the first comment under the post.

Upvotes: 4

user500592
user500592

Reputation:

The update method is supplied by the type UpdateInvoker. An instance of that type can be implicitly created from a Query by the methods productQueryToUpdateInvoker and/or tableQueryToUpdateInvoker (found in the BasicProfile), if they are in scope.

Now the type of your findById method is not a Query but a BasicQueryTemplate[Long, Group]. Looking at the docs, I can find no way from a BasicQueryTemplate (which is a subtype of StatementInvoker) to an UpdateInvoker, neither implicit nor explicit. Thinking about it, that makes kinda sense to me, since I understand a query template (invoker) to be something that has already been "compiled" from an abstract syntax tree (Query) to a prepared statement rather early, before parameterization, whereas an update invoker can only be built from an abstract syntax tree, i.e. a Query object, because it needs to analyze the query and extract its parameters/columns. At least that's the way it appears to work at present.

With that in mind, a possible solution unfolds:

def findGById(id: Long) = for {
  g <- Groups; if g.id === id
} yield g

def updateGroupX(g: Group)(implicit session: Session) = findGById(g.id).update(g)

Where findById(id: Long) has the type Query[Groups, Group] which is converted by productQueryToUpdateInvoker to an UpdateInvoker[Group] on which the update method can finally be called.

Hope this helped.

Upvotes: 7

Related Questions