Scala slick update table after querying with a join

浪子不回头ぞ 提交于 2020-01-24 20:06:25

问题


I want to update a table but the row needs to be selected based on certain conditions. Following code compiles fine but throws run-time exception:

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[SlickException: A query for an UPDATE statement must resolve to a comprehension with a single table -- Unsupported shape: Comprehension s2, Some(Apply Function =), None, ConstArray(), None, None, None, None]]

Here is the function (the intention is to allow update only for qualified user):

def updateRecord(record: Record)(implicit loggedInUserId: User) = {
    val q = records.withFilter(_.id === record.id).join(companies).on(_.companyId === _.id).filter(_._2.userid === loggedInUserId)
    val recordToUpdate = q.map { case (r, c) => r }
    val action = for {
      c <- recordToUpdate.update(record)
    } yield (c)
    ... // there are other actions in the for comprehension, removed them for clarity

I thought the result of the map is a row from the records table (not a tuple) but the errors seems to be indicating that I am not updating a "single" table.

Or is there a better way of doing query + update?


回答1:


Yes, you seem to try updating both tables.

Maybe you should try something like

  def updateRecord(record: Record)(implicit loggedInUserId: User): Future[Int] = {
    val recordToUpdate = records.filter(_.id === record.id)

    val q = recordToUpdate
      .join(companies).on(_.companyId === _.id)
      .filter(_._2.userid === loggedInUserId)
      .exists

    val action = for {
      c <- recordToUpdate.update(record)
//      ...
    } yield c

    for {
      isLoggedIn <- db.run(q.result)
      if isLoggedIn
      c <- db.run(action)
    } yield c

  }

You can also try

  def updateRecord(record: Record)(implicit loggedInUserId: User): 
        DBIOAction[Int, NoStream, Read with Write with Transactional] = {
    val recordToUpdate = records.filter(_.id === record.id)

    val action =
      recordToUpdate
      .join(companies).on(_.companyId === _.id)
      .filter(_._2.userid === loggedInUserId)
      .exists
      .result

    (for {
      isLoggedIn <- action
      if isLoggedIn
      c <- recordToUpdate.update(record)
//    ...
    } yield c).transactionally
  }

Variant that should work without NoSuchElementException: Action.withFilter failed. Based on the answer.

  def updateRecord(record: Record)(implicit loggedInUserId: User): 
        DBIOAction[Int, NoStream, Read with Write with Transactional] = {
    val recordToUpdate = records.filter(_.id === record.id)

    val action =
      recordToUpdate
      .join(companies).on(_.companyId === _.id)
      .filter(_._2.userid === loggedInUserId)
      .exists
      .result

    action.flatMap {
      case true => for {
        c <- recordToUpdate.update(record)
      //    ...
      } yield c

      case false => DBIO.successful(0) /*DBIO.failed(new IllegalStateException)*/
    }.transactionally
  }


来源:https://stackoverflow.com/questions/46260034/scala-slick-update-table-after-querying-with-a-join

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!