【问题标题】:Scala/Slick: how to query tables and return sequence of custom objectsScala/Slick:如何查询表并返回自定义对象的序列
【发布时间】:2016-10-19 12:32:52
【问题描述】:

我对 scala 和 slick 还很陌生。我无法从此函数中获取联系人序列。每个联系人对象都有名字、姓氏和一系列电话号码。姓名和电话号码使用外键存储在各自的表中。

  def testContacts: Future[Seq[Contact]] = {
    names.result.map { namelist =>
      var contacts = Seq[Contact]()
      for (n <- namelist) {            
        println("NAME: " + n) // THIS IS PRINTED!
        val phoneNumbers = for {
          p <- phones.filter(_.nameId === n.id)
        } yield p.phoneNumber
        phoneNumbers.result.map { plist =>
          contacts = contacts :+  Contact(n.firstName, n.lastName, plist)
          // Q: HOW DO I RETURN "contacts" to the caller?
          println("CONTACTS: " + plist) // THIS IS **NOT** PRINTED!
        }
      }
    }
    Future { Seq[Contact]() } // dummy statement to avoid compilation error
  }

从控制台:

scala> Await.result(db.run(names.result), 1000 毫秒) res9: Seq[Example.NameTable#TableElementType] = Vector(Name(George,W,1), Name(John,A,2))

scala> Await.result(db.run(phones.result), 1000 毫秒) res10:Seq[Example.PhoneTable#TableElementType] = 矢量(电话(+1 301 531 1121,1,1),电话(+1 301 748 5192,1,2),电话(+1 301 531 4519,2,3 ), 电话(+1 202 667 9612,2,4), 电话(+1 202 667 4044,2,5))

scala> Await.result(testContacts, 1000 毫秒) res5: Seq[Contacts.Contact] = List()

斯卡拉>

感谢任何帮助。这是完整的源代码:

import scala.concurrent.Await
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import slick.driver.H2Driver.api._
import scala.util.Try
import scala.concurrent.Future

object Contacts extends App {

  val db = Database.forConfig("dbconfig")

  case class Contact(firstName: String, lastName: String, phones: Seq[String])

  def testContacts: Future[Seq[Contact]] = {

    names.result.map { namelist =>
      var contacts = Seq[Contact]()
      for (n <- namelist) {
        println("NAME: " + n)
        val phoneNumbers = for {
          p <- phones.filter(_.nameId === n.id)
        } yield p.phoneNumber
        phoneNumbers.result.map { plist =>
          contacts = contacts :+  Contact(n.firstName, n.lastName, plist)
          // Q: HOW DO I RETURN "contacts" to the caller?
          println("CONTACTS: " + plist) // THIS IS NOT PRINTED!
        }
      }
    }
    Future { Seq[Contact]() } // dummy statement to avoid compilation error
  }

  case class Name(firstName: String, lastName: String, id: Long = 0L)

  class NameTable(tag: Tag) extends Table[Name](tag, "Names") {

    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
    def firstName = column[String]("FIRSTNAME")
    def lastName = column[String]("LASTNAME")

    override def * = (firstName, lastName, id) <> (Name.tupled, Name.unapply)
  }

  lazy val names = TableQuery[NameTable]

  case class Phone(phoneNumber: String, nameId: Long, id: Long = 0L)

  class PhoneTable(tag: Tag) extends Table[Phone](tag, "Phones") {

    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
    def phoneNumber = column[String]("PHONE_NUMBER")
    def nameId = column[Long]("NAME")

    override def * = (phoneNumber, nameId, id) <> (Phone.tupled, Phone.unapply)

    def name_fk = foreignKey("Phones_Names_ID_FK", nameId, names)(_.id, onUpdate = ForeignKeyAction.Cascade, onDelete = ForeignKeyAction.Cascade)
  }

  lazy val phones = TableQuery[PhoneTable]

  def testNames = Seq(
    Name("George", "W"),
    Name("John", "A"))

  def testPhones = Seq(
    Phone("+1 301 531 1121", 1L),
    Phone("+1 301 748 5192", 1L),
    Phone("+1 301 531 4519", 2L),
    Phone("+1 202 667 9612", 2L),
    Phone("+1 202 667 4044", 2L))

  def populate: DBIOAction[Option[Int], NoStream,Effect.All] =  {
    for {    
      _ <- names.schema.drop.asTry andThen names.schema.create
      _ <- phones.schema.drop.asTry andThen phones.schema.create
      nameCount <- names ++= testNames
      phoneCount <- phones ++= testPhones
    } yield nameCount
  }
}

【问题讨论】:

    标签: scala slick


    【解决方案1】:

    您的 testContacts 查询不是很直接,我会在查询语句中使用连接或过滤器,然后展开该行

    这就是我要尝试的方法

    def testContacts: Future[Seq[Contact]] = {
      val query = for {
        (n, pl) <- names join phones on (_.id === _.nameId)
      } yield (n, pl)
      db.run(query.result).map { (row) =>
        row.groupBy(_._1).map { r =>
          val name = r._1
          val phones = r._2.map(_._2.phoneNumber)
          Contact(name.firstName, name.lastName, phones)
        }.toSeq
      }
    }
    

    这使用光滑的forcomp 连接语法,然后映射结果

    【讨论】:

    • 非常感谢!我能够得到想要的结果。但是,当我尝试从 scala 控制台调用函数“testContacts”时,在 Example$.testContacts(main.scala:312) 处出现错误“scala> testContacts java.lang.NullPointerException ... 43 elided 如果我手动输入 val 查询= .. 和 db.run 语句我可以正确获取联系人的顺序。知道吗?第 312 行指向 db.run [为混乱的评论道歉,无法输入新行]
    • 我不知道具体,我从控制台运行了一个类似的 NPE。您可以发布您的新代码文件,以便我可以将异常行与您的示例匹配
    • 当我在我的代码中使用它时它仍然有效。目前不太担心控制台错误。但是,我有这个问题的一个变种并打开了一个新问题 - stackoverflow.com/questions/40144647/…
    猜你喜欢
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 2015-07-24
    • 1970-01-01
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    相关资源
    最近更新 更多