2016-09-07 61 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