2016-09-07 60 views
0

我有一些理解循環。這裏使用的對象被自動從DB產生具有slick.codegen.SourceCodeGenerator:TableQuery到案例類

for { 
    boxer <- Boxers.filter { b => b.address === someAddress } 
    fullBoxer <- buildFullBoxer(boxer) 
} yield { 
    fullBoxer 
} 

buildFullBoxer函數接受的情況下類BoxersRow作爲參數,因此該循環不編譯並生成錯誤:

type mismatch; found : models.Tables.Boxers required: models.Tables.BoxersRow

生成的模式代碼:

case class BoxersRow(id: Long, firstName: String, lastName: String, nick: Option[String] = None, boxingTypeId: Int = 0, birthDate: Option[java.sql.Date] = None, address: Option[String] = None, lastUpdated: java.sql.Timestamp) 
implicit def GetResultBoxersRow(implicit e0: GR[Long], e1: GR[String], e2: GR[Option[String]], e3: GR[Int], e4: GR[Option[java.sql.Date]], e5: GR[java.sql.Timestamp]): GR[BoxersRow] = GR{ 
    prs => import prs._ 
    BoxersRow.tupled((<<[Long], <<[String], <<[String], <<?[String], <<[Int], <<?[java.sql.Date], <<?[String], <<[java.sql.Timestamp])) 
} 
class Boxers(_tableTag: Tag) extends Table[BoxersRow](_tableTag, "boxers") { 
    def * = (id, firstName, lastName, nick, boxingTypeId, birthDate, address, lastUpdated) <> (BoxersRow.tupled, BoxersRow.unapply) 
    def ? = (Rep.Some(id), Rep.Some(firstName), Rep.Some(lastName), nick, Rep.Some(boxingTypeId), birthDate, address, Rep.Some(lastUpdated)).shaped.<>({r=>import r._; _1.map(_=> BoxersRow.tupled((_1.get, _2.get, _3.get, _4, _5.get, _6, _7, _8.get)))}, (_:Any) => throw new Exception("Inserting into ? projection not supported.")) 

    val id: Rep[Long] = column[Long]("id", O.AutoInc, O.PrimaryKey) 
    .... 
} 
lazy val Boxers = new TableQuery(tag => new Boxers(tag)) 

我當然不希望自動改變GE神經架構對象。 buildFullBoxer函數從數據庫讀取附加數據並構建一個包含所有必要數據的常見FullBoxer對象。

private def buildFullBoxer(boxersRow: BoxersRow): DBIO[FullBoxer] = { 
    val query = for { 
     ((((boxer, fight), division), b1), b2) <- 
     Boxers.filter(_.id === boxersRow.id) 
     .joinLeft(Fights).on((b, f) => (b.id === f.firstBoxerId) || (b.id === f.secondBoxerId)) 
     .joinLeft(Divisions).on((bf, d) => bf._2.map { _.divisionId === d.id }) 
     .joinLeft(Boxers).on((bfd, b1) => bfd._1._2.map { _.firstBoxerId === b1.id }) 
     .joinLeft(Boxers).on((bfdb1, b2) => bfdb1._1._1._2.map { _.secondBoxerId === b2.id }) 
    } yield (boxer, fight, division, b1, b2) 

    val action = query.result.map {case sequence => sequence.groupBy(x => x._1) }. 
     map { _.map { case (box, tup) => (box, tup.map { case (b, f, d, b1, b2) => f.map { fight => (fight, d.getOrElse(throw NoDivisionException("No such a division: " + fight.divisionId)), b1.getOrElse(throw NoBoxerException("No boxer with id " + fight.firstBoxerId, Seq.empty, None)), b2.getOrElse(throw NoBoxerException("No boxer with id " + fight.secondBoxerId, Seq.empty, None)), Seq.empty) } } map (_.map(FullFight.tupled)) flatten) } toSeq }. 
     map {_.map(FullBoxer.tupled).head } 
    action 
} 

我怎麼能通過案例類BoxersRow buildFullBoxer函數在這理解循環?

問候!

回答

1

我在這裏增加一個答案,因爲你不採取浮油查詢功能的優勢。工作流程一般是查詢 - >操作 - >結果。作爲一個經驗法則,儘可能保持低。這意味着與Query工作,直到不再可能。然後結合DBIOAction,最後如果你真的必須,開始結合Future(結果)。

一個光滑的核心功能是,你可以結合和混合幾個查詢到一個。一些在普通SQL中不可能的東西。您的用例可以通過混合兩個查詢輕鬆解決。它看起來是這樣的(未測試的代碼前面):

object BoxerQuery { 
    val findFullBoxers = { 
    Boxers 
     .joinLeft(Fights).on((b, f) => (b.id === f.firstBoxerId) || (b.id === f.secondBoxerId)) 
     .joinLeft(Divisions).on((bf, d) => bf._2.map { _.divisionId === d.id }) 
     .joinLeft(Boxers).on((bfd, b1) => bfd._1._2.map { _.firstBoxerId === b1.id }) 
     .joinLeft(Boxers).on((bfdb1, b2) => bfdb1._1._1._2.map { _.secondBoxerId === b2.id }) 
     .map { 
     case ((((boxer, fight), division), b1), b2) => (boxer, fight, division, b1, b2) 
     } 
    } 

    def findFullBoxerByAddress(address: Rep[Address]) = findFullBoxers.filter(fbQuery => fbQuery._1.address === address) 

    def findFullBoxerByWebaddress(webaddress: Rep[Webaddress] = findFullBoxers.filter(fbQuery => fbQuery._1.webaddress === webaddress) 
} 

上面的整個代碼塊仍然在Query水平。只要你喜歡,你可以混合和組合查詢。一個體面的IDE在這裏很有幫助。如果查詢最終返回你所需要的,創建一個返回FullBoxer一個動作:

val action: DBIOAction[Seq[FullBoxer]] = Query.findFullBoxerByAddress(address).result.map(_.groupBy(_._1).map { 
    case (boxer, grp) => FullBoxer(
    boxer, 
    grp.flatMap(_._2).distinct, 
    grp.flatMap(_._3).distinct, 
    grp.flatMap(_._4).distinct 
) 
}) 

現在我們已經一切就緒,運行action針對數據庫,並在單次往返獲取所有FullBoxer對象:

val fullBoxers: Future[Seq[FullBoxer]] = db.run(action) 
+0

感謝您的及時回覆:)它的作品! – Gandalf

0

Boxers.filter { b => b.address === someAddress }只返回一個查詢,而不是結果。查詢可以被組合和擴展。想想寫一個強類型的SQL查詢。爲了獲得結果集,您需要針對數據庫運行查詢分別操作。

所以首先你需要創建DBIOActionBoxers.filter(b => b.address === someAddress).result。行動可以再次組成,但不再延伸。

其次對數據庫運行該操作:db.run(Boxers.filter(b => b.address === someAddress).result),而dbDatabase對象(請參閱Slick docs)。 db.run終於返回s Future[Seq[BoxerRow]]

您可以直接使用map然後運行buildFullBoxer

val fullBoxers: Future[Seq[WhateverBuildFullBoxerReturns]] = { 
    db.run(Boxers.filter(b => b.address === someAddress).result).map { results => 
    results.map(boxer => buildFullBoxer(boxer)) 
    } 
} 
+0

正是!但是它會返回類似DBIOAction [Seq [DBIOAction [Option [FullBoxer]]]],而DBIOAction [Seq [Option [FullBoxer]]]會更好。我正在嘗試使用flatMap而不是map。 – Gandalf

+0

如果添加更多關於'buildFullBoxer()'的信息可能會有所幫助。什麼是簽名?這種方法的目的是什麼?你想從數據庫中獲取更多信息嗎? – Roman

+0

添加了buildFullBoxer函數的定義。它用於從數據庫讀取附加數據,並構建一個包含所有數據的通用對象。現在findbyWebbaddres函數的結果是DBIOAction [Seq [MySQLDriverapi.DBIO [FullBoxer]] – Gandalf