我有一个数据库结构,在Dream
和Tag
之间具有多对多关系。>>
[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
来检索具有Dream
的Tag
,但是我努力以一种完全非阻塞的方式进行操作
这是我执行检索的代码,因为这可能使您有所了解:
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)
场景
现在,我正处在想要更新Dream
和Tag
列表的阶段,我正在苦苦挣扎。
考虑到现有标签列表为["one", "two", "three"]
并被更新为["two", "three", "four"]
的情况,我想:
Tag
引用,则将Dream
删除为“一个”。Tag
和DreamTag
条目已经存在。Tag
“四个”,并为其添加一个条目到联接表中。我认为我需要执行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之间具有多对多关系。梦和标签保存在单独的表中,并且在这种情况下,它们之间通常有一个连接表...
我努力以一种完全不阻塞的方式进行。