1

I would like to perform a database insert with some ids taken from previous DB reading. All DB objects generated with slick.codegen.SourceCodeGenerator. I have the following reading function:

def findByName(firstName: String, lastName: String): Future[Option[PersonsRow]] = {
    db.run(Persons.filter(person => (person.firstName === firstName && person.lastName === lastName)).result.headOption)
}

I have to perform such a reading twice - because in my second table I have a person1 and person2. So in the end I have got 2 objects of the class Future[Option[PersonsRow]]. Then I would like to perform some insert:

def createMatch(firstPerson: PersonsRow, secondPerson: PersonsRow, eventDate: java.sql.Date): Future[Long] = {
  val match = MatchesRow(id = 0, firstPersonId = firstPerson.id, secondPersonId = secondPerson.id, date = eventDate)
  db.run(Matches returning Matches.map(_.id) += match)
}

I have heard that the best way to deal with Future objects is just to pass this object further without any explicit waiting for the Future complete. However I guess there is no possibility to pass Future here and I have to use some blocking code to get this Future's values here.

I even prepared a function:

def getFutureValue(person: Future[Option[PersonsRow]]): Option[PersonsRow] = {
  val emptyPersonsRow = PersonsRow(0, ...)  //empty person
  person onComplete {
    case Success(bsucc) => {
      return bsucc
    }
    case Failure(bfail) => {
      Logger.warn("There are problems with reading person from database: " + bfail.toString)
    }
  }
  return Some(emptyPersonsRow)
}

But it seems not to be working properly (returning person is always an emptyPersonsRow). Besides I believe that I use it wrong - since this Future mechanism is my great enemy and not a friend here :)

Any suggestion to put me in the right direction? Best Regards

Gandalf
  • 155
  • 1
  • 12

1 Answers1

1

For-comprehensions for the win

for {
  user1 <- findByName("John","Doe") map {_.getOrElse(throw new Error("No user 1"))}
  user2 <- findByName("Johnny","Doe") map {_.getOrElse(throw new Error("No user 2"))}
  resMatch <- createMatch(user1,user2,myEventDate)
} yield resMatch
John K
  • 1,285
  • 6
  • 18
  • I am dealing with some stupid error. Logging and debugging claims that only the first line of this for loop is being invoked. So it only invokes user1 <- findByName("John","Doe") map {_.getOrElse(throw new Error("No user 1"))} – Gandalf Jun 22 '16 at 14:07
  • Did you try findByName of the first line separately? Your error would suggest that the first user was not found (aka that findByName returned `None`). – John K Jun 23 '16 at 12:43
  • It seems that even if I make a simpler example: val user = for { user1 <- findByName("John","Doe") map {_.getOrElse(throw new Error("No user 1"))} } yield user1 println(user1) The user1 is always empty. If I put Thread sleep 1000 before the println - the user is loaded correctly. Seems that this 'for loop' does not wait for the statement to run and that's why all the operation does not work. – Gandalf Jun 23 '16 at 15:09
  • For loops do wait when it comes to futures. You have many SO questions about it, for example [this](http://stackoverflow.com/questions/19045936/scalas-for-comprehension-with-futures).In your test `println` doesn't run on the same thread as the future is executed before user1 is actually returned/changed. The correct test would be, with the same definition of `user`, to do `user map prinln` – John K Jun 23 '16 at 15:26
  • It's extremely confusing for me. Each invocation gives me different logs (not only the logs order but the content of this logs as well). Different users are loaded (and there are different exceptions claiming that there are not such an user). It seems that I have DB inserts invoked only when I use Thread sleep. Maybe that's because I have this `for { ... } yield` group inside another loop `for (event <- someEvents)` – Gandalf Jun 24 '16 at 09:52
  • ah yes!! The `for yield` returns a `Future`. So assign this `for yield` to a variable (that becomes a `Future`), then chain it with other `Future`s a needed. It would also look like you need a catchup on Scala Futures, check [this one](http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html) (I just love it) – John K Jun 24 '16 at 10:07
  • Even replacing this main `for (event <- someEvents)` comprehension with `someEvents.foreach { event =>` loop it looks not better. When I use `val result = for { ... } yield resMatch` and invoke: `result onComplete { case Success(id) => Logger.info("Great success: " + id) case Failure(t) => Logger.warn("An error has occured: " + t.getMessage)}` not all exceptions are begin shown (MySQL constraint errors are not shown). Only the `Thread sleep` helps here. – Gandalf Jun 24 '16 at 10:27
  • Post full current code? I don't want to presume too much, but do you feel comfortable with futures now? If not have you tried the provided link? – John K Jun 24 '16 at 16:14
  • I pushed my full current code here: [link](https://github.com/rkoziol7/Robs/blob/master/donring/app/standalone/PinnacleParser.scala). `var boxingEvents = ...` is just a Seq[BoxingEvents] I have read the tuturial you recommended together with many others. Theoretically I should know everything. However I still do not understand how my code works. – Gandalf Jun 25 '16 at 10:51