使用 Slick 执行并发查询时出现 NullPointerException
NullPointerException on executing concurrent queries using Slick
我正在使用 Postgres 9.3
和 Slick 3.1.1
开发 Scala 应用程序。当多个查询同时执行时,我在 slick 驱动程序上得到 Null Pointer Exception。
这是我的简化代码。我正在创建多个参与者,它们将调用相同的方法从数据库中查询。
package com.app.repo
import java.sql.Timestamp
import akka.actor.{Actor, ActorSystem, Props}
import slick.driver.PostgresDriver
import slick.driver.PostgresDriver.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.FiniteDuration
import scala.util.{Failure, Success}
case class SampleData(id: Long, name: String, createDate: java.sql.Timestamp)
object Tables extends {
val profile = PostgresDriver
} with Tables
trait Tables {
val profile: PostgresDriver
import profile.api._
class SampleDataTable(_tableTag: Tag) extends Table[SampleData](_tableTag, Some("processing"), "SampleData") {
def * = (id, name, createDate) <>(SampleData.tupled, SampleData.unapply)
def ? = (Rep.Some(id), Rep.Some(name), Rep.Some(createDate)).shaped.<>({ r => import r._; _1.map(_ => SampleData.tupled((_1.get, _2.get, _3.get))) }, (_: Any) => throw new Exception("Inserting into ? projection not supported."))
val id: Rep[Long] = column[Long]("SampleId", O.AutoInc, O.PrimaryKey)
val name: Rep[String] = column[String]("Name")
val createDate: Rep[java.sql.Timestamp] = column[java.sql.Timestamp]("CreateDate")
}
lazy val sampleDataTable = new TableQuery(tag => new SampleDataTable(tag))
}
class SampleQueryingActor(delay: FiniteDuration, duration: FiniteDuration) extends Actor {
import scala.concurrent.duration._
override def preStart() = {
context.system.scheduler.schedule(0.second, duration, self, "tick")
}
override def receive: Receive = {
case "tick" => {
println("tick received.. ")
//val range = 1 until 1000
RepositoryImpl.reader.onComplete({
case Success(r) => println(s"got sum as ${r.getOrElse(0)}")
case Failure(ex) => ex.printStackTrace()
})
}
}
}
object DriverHelper {
val user = "postgres"
val url = "jdbc:postgresql://192.168.1.50:5432/MyDatabase"
val password = "password"
val jdbcDriver = "org.postgresql.Driver"
val db: PostgresDriver.backend.DatabaseDef = Database.forURL(url, user = user, password = password, driver = jdbcDriver)
}
object RepositoryImpl {
val db: PostgresDriver.backend.DatabaseDef = DriverHelper.db
val now = new Timestamp(System.currentTimeMillis())
def reader = {
db.run(Tables.sampleDataTable.filter(_.createDate > now).map(_.id).sum.result)
}
def insertBatchRecords(list: List[SampleData]) = {
db.run(Tables.sampleDataTable ++= list)
}
}
object PGConnectionTester extends App {
import scala.concurrent.duration._
val sys = ActorSystem("sys")
sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
}
当我执行上面的代码时,出现如下错误:
java.lang.NullPointerException
at slick.jdbc.DriverDataSource.getConnection(DriverDataSource.scala:98)
at slick.jdbc.DataSourceJdbcDataSource.createConnection(JdbcDataSource.scala:64)
at slick.jdbc.JdbcBackend$BaseSession.conn$lzycompute(JdbcBackend.scala:415)
at slick.jdbc.JdbcBackend$BaseSession.conn(JdbcBackend.scala:414)
at slick.jdbc.JdbcBackend$SessionDef$class.prepareStatement(JdbcBackend.scala:297)
at slick.jdbc.JdbcBackend$BaseSession.prepareStatement(JdbcBackend.scala:407)
at slick.jdbc.StatementInvoker.results(StatementInvoker.scala:33)
at slick.jdbc.StatementInvoker.iteratorTo(StatementInvoker.scala:22)
at slick.jdbc.Invoker$class.first(Invoker.scala:31)
at slick.jdbc.StatementInvoker.first(StatementInvoker.scala:16)
at slick.driver.JdbcActionComponent$QueryActionExtensionMethodsImpl$$anon.run(JdbcActionComponent.scala:228)
at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:32)
at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:29)
at slick.backend.DatabaseComponent$DatabaseDef$$anon.liftedTree1(DatabaseComponent.scala:237)
at slick.backend.DatabaseComponent$DatabaseDef$$anon.run(DatabaseComponent.scala:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
演员将每 10 秒调用一次相同的方法。但是,我只是第一次收到此错误。之后查询正确执行。我不明白为什么会这样。在这个示例案例中,只有一些简单的读取操作。但在我的实际情况中,由于查询失败,一些数据在没有正确处理的情况下丢失了。
这个错误与连接池有关吗?
我认为您已经发现 this 问题。尝试对 db 使用 lazy val,这样它只初始化一次:
object DriverHelper {
val user = "postgres"
val url = "jdbc:postgresql://192.168.1.50:5432/MyDatabase"
val password = "password"
val jdbcDriver = "org.postgresql.Driver"
lazy val db: PostgresDriver.backend.DatabaseDef = Database.forURL(url, user = user, password = password, driver = jdbcDriver)
}
只是为遇到此问题的其他人分享信息。
Slick 本身存在错误。据报道here。 Git 用户 mustajavi 修复了这个问题并合并到最新的 Slick 分支。随着 3.1.1 的最新更新,这个问题对我来说已经解决了。
Git中心的相关链接:
我正在使用 Postgres 9.3
和 Slick 3.1.1
开发 Scala 应用程序。当多个查询同时执行时,我在 slick 驱动程序上得到 Null Pointer Exception。
这是我的简化代码。我正在创建多个参与者,它们将调用相同的方法从数据库中查询。
package com.app.repo
import java.sql.Timestamp
import akka.actor.{Actor, ActorSystem, Props}
import slick.driver.PostgresDriver
import slick.driver.PostgresDriver.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration.FiniteDuration
import scala.util.{Failure, Success}
case class SampleData(id: Long, name: String, createDate: java.sql.Timestamp)
object Tables extends {
val profile = PostgresDriver
} with Tables
trait Tables {
val profile: PostgresDriver
import profile.api._
class SampleDataTable(_tableTag: Tag) extends Table[SampleData](_tableTag, Some("processing"), "SampleData") {
def * = (id, name, createDate) <>(SampleData.tupled, SampleData.unapply)
def ? = (Rep.Some(id), Rep.Some(name), Rep.Some(createDate)).shaped.<>({ r => import r._; _1.map(_ => SampleData.tupled((_1.get, _2.get, _3.get))) }, (_: Any) => throw new Exception("Inserting into ? projection not supported."))
val id: Rep[Long] = column[Long]("SampleId", O.AutoInc, O.PrimaryKey)
val name: Rep[String] = column[String]("Name")
val createDate: Rep[java.sql.Timestamp] = column[java.sql.Timestamp]("CreateDate")
}
lazy val sampleDataTable = new TableQuery(tag => new SampleDataTable(tag))
}
class SampleQueryingActor(delay: FiniteDuration, duration: FiniteDuration) extends Actor {
import scala.concurrent.duration._
override def preStart() = {
context.system.scheduler.schedule(0.second, duration, self, "tick")
}
override def receive: Receive = {
case "tick" => {
println("tick received.. ")
//val range = 1 until 1000
RepositoryImpl.reader.onComplete({
case Success(r) => println(s"got sum as ${r.getOrElse(0)}")
case Failure(ex) => ex.printStackTrace()
})
}
}
}
object DriverHelper {
val user = "postgres"
val url = "jdbc:postgresql://192.168.1.50:5432/MyDatabase"
val password = "password"
val jdbcDriver = "org.postgresql.Driver"
val db: PostgresDriver.backend.DatabaseDef = Database.forURL(url, user = user, password = password, driver = jdbcDriver)
}
object RepositoryImpl {
val db: PostgresDriver.backend.DatabaseDef = DriverHelper.db
val now = new Timestamp(System.currentTimeMillis())
def reader = {
db.run(Tables.sampleDataTable.filter(_.createDate > now).map(_.id).sum.result)
}
def insertBatchRecords(list: List[SampleData]) = {
db.run(Tables.sampleDataTable ++= list)
}
}
object PGConnectionTester extends App {
import scala.concurrent.duration._
val sys = ActorSystem("sys")
sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
sys.actorOf(Props(classOf[SampleQueryingActor], 1.seconds, 10.seconds))
}
当我执行上面的代码时,出现如下错误:
java.lang.NullPointerException
at slick.jdbc.DriverDataSource.getConnection(DriverDataSource.scala:98)
at slick.jdbc.DataSourceJdbcDataSource.createConnection(JdbcDataSource.scala:64)
at slick.jdbc.JdbcBackend$BaseSession.conn$lzycompute(JdbcBackend.scala:415)
at slick.jdbc.JdbcBackend$BaseSession.conn(JdbcBackend.scala:414)
at slick.jdbc.JdbcBackend$SessionDef$class.prepareStatement(JdbcBackend.scala:297)
at slick.jdbc.JdbcBackend$BaseSession.prepareStatement(JdbcBackend.scala:407)
at slick.jdbc.StatementInvoker.results(StatementInvoker.scala:33)
at slick.jdbc.StatementInvoker.iteratorTo(StatementInvoker.scala:22)
at slick.jdbc.Invoker$class.first(Invoker.scala:31)
at slick.jdbc.StatementInvoker.first(StatementInvoker.scala:16)
at slick.driver.JdbcActionComponent$QueryActionExtensionMethodsImpl$$anon.run(JdbcActionComponent.scala:228)
at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:32)
at slick.driver.JdbcActionComponent$SimpleJdbcDriverAction.run(JdbcActionComponent.scala:29)
at slick.backend.DatabaseComponent$DatabaseDef$$anon.liftedTree1(DatabaseComponent.scala:237)
at slick.backend.DatabaseComponent$DatabaseDef$$anon.run(DatabaseComponent.scala:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
演员将每 10 秒调用一次相同的方法。但是,我只是第一次收到此错误。之后查询正确执行。我不明白为什么会这样。在这个示例案例中,只有一些简单的读取操作。但在我的实际情况中,由于查询失败,一些数据在没有正确处理的情况下丢失了。 这个错误与连接池有关吗?
我认为您已经发现 this 问题。尝试对 db 使用 lazy val,这样它只初始化一次:
object DriverHelper {
val user = "postgres"
val url = "jdbc:postgresql://192.168.1.50:5432/MyDatabase"
val password = "password"
val jdbcDriver = "org.postgresql.Driver"
lazy val db: PostgresDriver.backend.DatabaseDef = Database.forURL(url, user = user, password = password, driver = jdbcDriver)
}
只是为遇到此问题的其他人分享信息。
Slick 本身存在错误。据报道here。 Git 用户 mustajavi 修复了这个问题并合并到最新的 Slick 分支。随着 3.1.1 的最新更新,这个问题对我来说已经解决了。
Git中心的相关链接: