光滑 - 多对多的关系

时间:2018-02-05 06:31:22

标签: scala slick

我正在开发一个图书馆数据模型,其中每本书可以有多个作者,反之亦然(多对多)。 我想将一系列书籍传递给html视图页面,每本书都包含其作者列表。 为此,我为书籍和作者定义了以下表格:

  private class BookTable(tag: Tag) extends Table[Book](tag, "book") {

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

    def name = column[String]("name")

    def publishDate = column[Date]("publish_date")

    def memberId = column[Option[Long]]("member_id")

    def member = foreignKey("member_fk",memberId,members)(_.id)

    type Data = (Long, String, Date, Option[Long])

    def constructBook: Data => Book = {
      case (id, name, publishDate, memberId) =>
        Book(id, name, publishDate, memberId)
    }

    def extractBook: PartialFunction[Book, Data] = {
      case Book(id, name, publishDate, memberId, _) =>
        (id, name, publishDate, memberId)
    }

    def * = (id, name, publishDate, memberId) <> (constructBook, extractBook.lift)
  }


private class AuthorBookTable (tag: Tag) extends Table[AuthorBook](tag, "author_book") {


 def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

 def authorId = column[Long]("author_id")

 def bookId = column[Long]("book_id")

  def memberId = column[Option[Long]]("member_id")

  def author = foreignKey("author_fk",authorId,authors)(_.id)

  def book = foreignKey("book_fk",bookId,books)(_.id)

 def * = (id, authorId, bookId) <> ((AuthorBook.apply _).tupled, AuthorBook.unapply)
}


private class AuthorTable (tag: Tag) extends Table[Author](tag, "author") {

 def id = column[Long]("id", O.PrimaryKey, O.AutoInc)

 def name = column[String]("name")

 def * = (id, name) <> ((Author.apply _).tupled, Author.unapply)
}

书籍案例类如下:

case class Book(id: Long, name: String, publishDate: Date, memberId: Option[Long] = None, authors: Seq[Author]= Seq.empty)
{

  def updateAuthors(authorss: Seq[Author]) = {
    this.copy(authors=authorss)
  }
}

在控制器中我使用以下内容:

  def getBooks = Action.async { implicit request =>
    repo.getBooks.map { books =>
      val booksWithAuthors=books.map( b=> {val updateB=b.updateAuthors( repo.getBookAuthors(b.id))
        updateB})
      Ok(Json.toJson(booksWithAuthors))
    }
  }

我的问题是关于下面显示的getBookAuthors实现:

  implicit def waitForFuture[A](f:Future[A]) = {
    def res: A = Await.result(f, Duration.Inf)
    res
  }

  def getBookAuthors(id: Long): Seq[Author] = {
    val result=db.run {
      val innerJoin = for {
        (ab, a) <- authorBooks join authors on (_.authorId === _.id)
      } yield (a, ab.bookId)
      innerJoin.filter(_._2 === id).sortBy(_._1.name).map(_._1).result
    }
    waitForFuture(result)
  }

我担心的是getBookAuthors功能是阻塞的,我不确定它是不是最好的做法。请告知是否有更好的方法。

1 个答案:

答案 0 :(得分:0)

正如您所说,在此上下文中阻塞方法非常糟糕,您将失去使用非阻塞库作为Slick的优势。

getBookAuthors将按如下方式编写,返回需要在调用者中管理的Future[Seq[Author]]

def getBookAuthors(id: Long): Future[Seq[Author]] =
  db.run {
    val innerJoin = for {
      (ab, a) <- authorBooks join authors on (_.authorId === _.id)
    } yield (a, ab.bookId)
    innerJoin.filter(_._2 === id).sortBy(_._1.name).map(_._1).result
  }

所以调用者应该改写为:

def getBooks = Action.async { implicit request =>
  repo.getBooks.flatMap { books =>
    Future.sequence(
      books.map { b => 
        repo.getBookAuthors(b.id).map(authors => b.updateAuthors(authors))
      }
    ).map { booksWithAuthors =>
      Ok(Json.toJson(booksWithAuthors))
    }
  }
}

这意味着,一旦您拥有books: Seq[Book],您将在其上进行映射以集成作者,这将以Seq[Future[Book]]结束。 然后可以使用Future[Seq[Book]]方法将其转换为Future.sequence(带作者)。 最后,您需要flatMap外部FutureFuture[Future[Seq[Book]]]转移到更简单的Future[Seq[Book]]

第二个片段可以更清晰的方式重构,利用for-comprehension <{1}}作为flatMap

的语法糖
private def addAuthorsToBooks(books: Seq[Book]): Future[Seq[Book]] = 
  Future.sequence(
    books.map { b => 
      repo.getBookAuthors(b.id).map(authors => b.updateAuthors(authors))
    }
  )

def getBooks = Action.async { implicit request =>
  for {
    books <- repo.getBooks
    booksWithAuthors <- addAuthorsToBooks(books)
  } yield Ok(Json.toJson(booksWithAuthors))
}