在Slick 3.0中更新多对多联接表

问题描述 投票:0回答:1

我有一个数据库结构,在DreamTag之间具有多对多关系。>>

[Dream s和Tag s被保存在单独的表中,在这种情况下,它们之间通常有一个连接表,而类DreamTag代表连接:

  protected class DreamTagTable(tag: Tag) extends Table[DreamTag](tag, "dreamtags") {

    def dreamId = column[Long]("dream_id")
    def dream = foreignKey("dreams", dreamId, dreams)(_.id)
    def tagId = column[Long]("tag_id")
    def tag = foreignKey("tags", tagId, tags)(_.id)

    // default projection
    def * = (dreamId, tagId) <> ((DreamTag.apply _).tupled, DreamTag.unapply)
  }

我已经设法执行适当的双精度JOIN来检索具有DreamTag,但是我努力以一种完全非阻塞的方式进行操作

这是我执行检索的代码,因为这可能使您有所了解:

  def createWithTags(form: DreamForm): Future[Seq[Int]] = db.run {
    logger.info(s"Creating dream [$form]")

    // action to put the dream
    val dreamAction: DBIO[Dream] =
      dreams.map(d => (d.title, d.content, d.date, d.userId, d.emotion))
        .returning(dreams.map(_.id))
        .into((fields, id) => Dream(id, fields._1, fields._2, fields._3, fields._4, fields._5))
        .+=((form.title, form.content, form.date, form.userId, form.emotion))

    // action to put any tags that don't already exist (create a single action)
    val tagActions: DBIO[Seq[MyTag]] =
      DBIO.sequence(form.tags.map(text => createTagIfNotExistsAction(text)))

    // zip allows us to get the results of both actions in a tuple
    val zipAction: DBIO[(Dream, Seq[MyTag])] = dreamAction.zip(tagActions)

    // put the entries into the join table, if the zipAction succeeds
    val dreamTagsAction = exec(zipAction.asTry) match {
      case Success(value) => value match {
        case (dream, tags) =>
          DBIO.sequence(tags.map(tag => createDreamTagAction(dream, tag)))
      }
      case Failure(exception) => throw exception
    }

    dreamTagsAction
  }

  private def createTagIfNotExistsAction(text: String): DBIO[MyTag] = {
    tags.filter(_.text === text).result.headOption.flatMap {
      case Some(t: MyTag) => DBIO.successful(t)
      case None =>
        tags.map(t => (t.text))
          .returning(tags.map(_.id))
          .into((text, id) => MyTag(id, text)) += text
    }
  }

  private def createDreamTagAction(dream: Dream, tag: MyTag): DBIO[Int] = {
    dreamTags += DreamTag(dream.id, tag.id)
  }

  /**
    * Helper method for executing an async action in a blocking way
    */
  private def exec[T](action: DBIO[T]): T = Await.result(db.run(action), 2.seconds)

场景

现在,我正处在想要更新DreamTag列表的阶段,我正在苦苦挣扎。

考虑到现有标签列表为["one", "two", "three"]并被更新为["two", "three", "four"]的情况,我想:

  1. 如果没有其他Tag引用,则将Dream删除为“一个”。
  2. 不触摸“两个”和“三个”的条目,因为TagDreamTag条目已经存在。
  3. 如果不存在,则创建Tag“四个”,并为其添加一个条目到联接表中。
  4. 我认为我需要执行list1.diff(list2)list2.diff(list1)之类的操作,但这需要首先执行get操作,这似乎是错误的。

也许我的想法是错误的-最好只是为此Dream清除联接表中的所有条目,然后在新列表中创建每个项目,或者有什么好方法来区分两个列表(上一个和现有的)并根据需要执行删除/添加?

感谢您的帮助。

N.B。是的,Tag是一个超级讨厌的类名,因为它与slick.lifted.Tag冲突!

更新-我的解决方案:

我去了理查德在回答中提到的选项2 ...

// action to put any tags that don't already exist (create a single action)
val tagActions: DBIO[Seq[MyTag]] =
  DBIO.sequence(form.tags.map(text => createTagIfNotExistsAction(text)))

// zip allows us to get the results of both actions in a tuple
val zipAction: DBIO[(Int, Seq[MyTag])] = dreamAction.zip(tagActions)

// first clear away the existing dreamtags
val deleteExistingDreamTags = dreamTags
  .filter(_.dreamId === dreamId)
  .delete

// put the entries into the join table, if the zipAction succeeds
val dreamTagsAction = zipAction.flatMap {
  case (_, tags) =>
    DBIO.sequence(tags.map(tag => createDreamTagAction(dreamId, tag)))
}

deleteExistingDreamTags.andThen(dreamTagsAction)

我的数据库结构在Dreams和Tag之间具有多对多关系。梦和标签保存在单独的表中,并且在这种情况下,它们之间通常有一个连接表...

scala playframework slick slick-3.0
1个回答
1
投票

我努力以一种完全不阻塞的方式进行。

© www.soinside.com 2019 - 2024. All rights reserved.