2016-09-07 3 views
0

이해력이있는 부분이 있습니다. 여기에 사용 된 객체가 자동으로 slick.codegen.SourceCodeGenerator으로 DB에서 생성 된 :TableQuery to case 클래스

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 함수는 DB에서 추가 데이터를 읽고 필요한 모든 데이터를 포함하는 공통 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 
} 

어떻게하면 이해할 수있는 루프를 위해 buildFullBoxer 함수로 사례 클래스 BoxersRow를 전달할 수 있습니까?

감사합니다.

답변

1

내가 여기에 다른 대답을 추가 해요. 일반적으로 워크 플로는 쿼리 -> 작업 -> 결과입니다. 경험에 비추어 가능한 한 낮게 유지하십시오. 더 이상 가능하지 않을 때까지 Query으로 작업해야합니다. 그런 다음 DBIOAction을 조합 한 다음 실제로해야한다면 Future (결과) 결합을 시작하십시오.

하나의 슬릭 핵심 기능 중 하나는 여러 쿼리를 결합하고 하나씩 조합 할 수 있다는 것입니다. 일반 SQL에서는 불가능합니다. 두 개의 쿼리를 혼합하여 유스 케이스를 쉽게 해결할 수 있습니다. 그것은 (앞서 테스트되지 않은 코드)과 같을 수 : 전체 코드 블록 위 Query 수준에 여전히

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) 
} 

. 원하는대로 쿼리를 혼합하고 조합 할 수 있습니다. 괜찮은 IDE가 꽤 도움이됩니다.우리는 장소에 모든 것을 가지고 이제

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 객체를 가져 오기 :

를 쿼리 마침내 당신이 필요로하는 FullBoxer를 반환하는 조치를 만들 반환하는 경우
val fullBoxers: Future[Seq[FullBoxer]] = db.run(action) 
+0

답장을 보내 주셔서 감사합니다 :) 그것은 작동합니다! – Gandalf

0

Boxers.filter { b => b.address === someAddress }은 결과가 아니라 단지 쿼리를 반환합니다. 쿼리를 작성하고 확장 할 수 있습니다. 강력한 형식의 SQL 쿼리를 작성한다고 생각하십시오. 결과 세트를 얻으려면 데이터베이스에 대해 각각 조치를 수행해야합니다.

먼저 DBIOAction : Boxers.filter(b => b.address === someAddress).result을 만들어야합니다. 작업은 다시 구성 할 수 있지만 더 이상 확장 할 수 없습니다.

데이터베이스에 대해이 작업을 두 번째로 실행하십시오 : db.run(Boxers.filter(b => b.address === someAddress).result)dbDatabase 개체입니다 (Slick docs 참조). db.run가 마침내 a 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 [Option [FullBoxer]]]가 더 좋을 때 DBIOAction [Seq [DBIOAction [Option [FullBoxer]]]와 같은 것을 반환합니다. map 대신 flatMap을 사용하려고합니다. – Gandalf

+0

'buildFullBoxer()'에 대한 정보를 더 추가하면 도움이 될 것입니다. 서명은 무엇입니까? 이 방법의 목적은 무엇입니까? DB에서 추가 정보를 가져 오려고합니까? – Roman

+0

buildFullBoxer 함수의 정의가 추가되었습니다. DB에서 추가 데이터를 읽고 모든 데이터를 포함하는 공통 객체를 작성하는 데 사용됩니다. 이제 findbyWebbaddres 함수의 결과는 DBIOAction입니다. [Seq [MySQLDriverapi.DBIO [FullBoxer]] – Gandalf