我正在使用Slick和PostgreSQL进行存储的Scala应用程序。我有一个方法foo
,它从CSV文件读取数据并将大约10,000行插入数据库。完成行插入的将来操作后,将调用另一个方法bar
,该方法将从数据库中检索这些行并对它们执行一些操作。这就是问题所在:因为在Future完成时尚未插入任何行,所以实际上没有从数据库中检索到行。
根据我在寻找答案和官方文档时所能收集的信息,在成功执行insert语句之前,不应完成Future。如果将以下代码Thread.sleep(30000)
添加到bar
,以允许首先执行insert语句,则这些方法将提供预期的结果。现在,出于明显的原因,我宁愿不这样做,所以我正在寻找替代方法。
下图说明了调用初始方法doStuff
后的程序流程:
doStuff
调用foo
来加载数据并将其存储在数据库中,然后返回Future。然后在doStuff
中映射此Future并调用bar
。 bar
栏从数据库中检索行并进行处理。但是,由于在调用bar
时未插入任何行,因此不会处理任何数据。
doStuff
方法:
def doStuff(csvFile: File): Future[Unit] = {
fooService.foo(csvFile)
.map(_ => {
csvFile.delete()
barService.bar()
})
}
foo
方法:
def foo(file: File) Future[Unit] = {
val reader = CSVReader.open(file)
fooStorage.truncateFooData().map(_ => {
val foos = for (line <- reader.iterator if
line.head != "bad1" &&
line.head !="bad2")
yield parseFooData(line)
fooStorage.saveFooDataBulk(foos.toSeq)
})
}
我如何使用Slick插入行:
override def saveFooDataBulk(fooSeq: Seq[Foo]): Future[Seq[Foo]] =
db.run(DBIO.seq(fooQuery ++= fooSeq)).map(_ => fooSeq)
我希望bar
在所有行都插入数据库后立即被调用,但是现在,Slick的Future完成得太早了。如果它是相关的:将请求发送到Akka Http端点时,将调用doStuff
方法。应用程序和数据库在两个不同的docker容器中运行。我在做什么错了?
我也无法摆脱一年半以前选择的用户名现在的适用性。
答案 0 :(得分:2)
在map
中用flatMap
替换def foo
。否则,它将启动Future[Seq[Foo]]
,然后立即返回()
,然后您的doStuff
将其丢弃。像这样:
fooStorage
.truncateFooData()
.flatMap(_ => {
/* stuff... */
fooStorage.saveFooDataBulk(foo.toSeq)
})
.map(_ => ())
我没有测试它,但是无论如何,在另一个Future
中间开始一些Future.map
,然后立即返回()
感觉并不对。