简体   繁体   中英

How can I connect a scala play application to postgres database

I'm trying to connect a basic play application to my remote postgres database. After overcoming a few walls, I'm now stuck on a seemingly unexplained exception when connecting. The application is barely more than that created from "activator new".

tl;dr

When creating database in play application, an exception is thrown:

[ClassNotFoundException: slick.jdbc.hikaricp.HikariCPJdbcDataSource$]

Details

Here's my simple application.scala :

package controllers

import play.api._
import play.api.mvc._
import play.api.Logger
import slick.jdbc.JdbcBackend.Database

class Application extends Controller {

  def index = Action {
    testdb()
    Ok(views.html.index("Your new application is ready."))
  }

  def testdb(): Unit = {
    val db = Database.forConfig("db.local")
  }
}

And my build.sbt :

name := """elophant"""

version := "0.0.1"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.6"

libraryDependencies ++= Seq(
  jdbc,
  cache,
  ws,
  specs2 % Test,
  "org.postgresql" % "postgresql" % "9.4-1204-jdbc4",
  "com.zaxxer" % "HikariCP" % "2.4.1",
  "com.typesafe.slick" %% "slick" % "3.1.0",
  "org.slf4j" % "slf4j-nop" % "1.6.4"
)

resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"

routesGenerator := InjectedRoutesGenerator

And application.conf :

play.crypto.secret = "xxx"
play.i18n.langs = [ "en" ]

db.local.driver: org.postgresql.Driver
db.local.url="jdbc:postgresql://"${?ELOPHANT_GATEWAY}":xxx/elophant"
db.local.username=elophantuser
db.local.password=${?ELOPHANT_USER_PASSWORD}

(Note: I verified that the above environment variables are all set correctly)

But when I hit the index action, I get the following exception:

[info] - application - Creating Pool for datasource 'local'
[info] - play.api.db.DefaultDBApi - Database [local] connected at jdbc:postgresql://174.1.53.209:19213/elophant
[info] - play.api.Play - Application started (Dev)
[error] - application -

! @6nnd5nh6h - Internal server error, for (GET) [/] ->

play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[ClassNotFoundException: slick.jdbc.hikaricp.HikariCPJdbcDataSource$]]
    at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:265) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:191) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.GlobalSettings$class.onError(GlobalSettings.scala:179) [play_2.11-2.4.3.jar:2.4.3]
    at play.api.DefaultGlobal$.onError(GlobalSettings.scala:212) [play_2.11-2.4.3.jar:2.4.3]
    at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:94) [play_2.11-2.4.3.jar:2.4.3]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:158) [play-netty-server_2.11-2.4.3.jar:2.4.3]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:155) [play-netty-server_2.11-2.4.3.jar:2.4.3]
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.6.jar:na]
    at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:215) [scala-library-2.11.6.jar:na]
    at scala.util.Try$.apply(Try.scala:191) [scala-library-2.11.6.jar:na]
    at scala.util.Failure.recover(Try.scala:215) [scala-library-2.11.6.jar:na]
    at scala.concurrent.Future$$anonfun$recover$1.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
    at scala.concurrent.Future$$anonfun$recover$1.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
    at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) [scala-library-2.11.6.jar:na]
    at play.api.libs.iteratee.Execution$trampoline$.executeScheduled(Execution.scala:109) [play-iteratees_2.11-2.4.3.jar:2.4.3]
    at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:71) [play-iteratees_2.11-2.4.3.jar:2.4.3]
    at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40) [scala-library-2.11.6.jar:na]
    at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248) [scala-library-2.11.6.jar:na]
    at scala.concurrent.Promise$class.complete(Promise.scala:55) [scala-library-2.11.6.jar:na]
    at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:153) [scala-library-2.11.6.jar:na]
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:23) [scala-library-2.11.6.jar:na]
    at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40) [akka-actor_2.11-2.3.13.jar:na]
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397) [akka-actor_2.11-2.3.13.jar:na]
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library-2.11.6.jar:na]
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library-2.11.6.jar:na]
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.6.jar:na]
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.6.jar:na]
Caused by: java.lang.ClassNotFoundException: slick.jdbc.hikaricp.HikariCPJdbcDataSource$
    at java.lang.ClassLoader.findClass(ClassLoader.java:530) ~[na:1.8.0_60]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_60]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_60]
    at slick.util.ClassLoaderUtil$$anon$1.loadClass(ClassLoaderUtil.scala:12) ~[slick_2.11-3.1.0.jar:na]
    at slick.jdbc.JdbcDataSource$.loadFactory$1(JdbcDataSource.scala:30) ~[slick_2.11-3.1.0.jar:na]
    at slick.jdbc.JdbcDataSource$.forConfig(JdbcDataSource.scala:35) ~[slick_2.11-3.1.0.jar:na]
    at slick.jdbc.JdbcBackend$DatabaseFactoryDef$class.forConfig(JdbcBackend.scala:268) ~[slick_2.11-3.1.0.jar:na]
    at slick.jdbc.JdbcBackend$$anon$3.forConfig(JdbcBackend.scala:33) ~[slick_2.11-3.1.0.jar:na]
    at controllers.Application.testdb(Application.scala:17) ~[classes/:na]
    at controllers.Application$$anonfun$index$1.apply(Application.scala:11) ~[classes/:na]
    at controllers.Application$$anonfun$index$1.apply(Application.scala:10) ~[classes/:na]
    at play.api.mvc.ActionBuilder$$anonfun$apply$17.apply(Action.scala:439) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.ActionBuilder$$anonfun$apply$17.apply(Action.scala:439) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.ActionBuilder$$anonfun$apply$16.apply(Action.scala:408) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.ActionBuilder$$anonfun$apply$16.apply(Action.scala:407) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$.invokeBlock(Action.scala:533) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$.invokeBlock(Action.scala:530) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.ActionBuilder$$anon$1.apply(Action.scala:493) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:104) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:103) ~[play_2.11-2.4.3.jar:2.4.3]
    at scala.Option.map(Option.scala:146) ~[scala-library-2.11.6.jar:na]
    at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:103) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96) ~[play_2.11-2.4.3.jar:2.4.3]
    at play.api.libs.iteratee.DoneIteratee$$anonfun$mapM$2.apply(Iteratee.scala:741) ~[play-iteratees_2.11-2.4.3.jar:2.4.3]
    at play.api.libs.iteratee.DoneIteratee$$anonfun$mapM$2.apply(Iteratee.scala:741) ~[play-iteratees_2.11-2.4.3.jar:2.4.3]
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) [scala-library-2.11.6.jar:na]
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) [scala-library-2.11.6.jar:na]
    ... 6 common frames omitted

Some googling suggested that I include that hikari library in my build.sbt, but it hasn't seemed to help (also, it's not in any of the documentation for play itself).

My java configuration skills are negligible, so any help would be appreciated!

I managed to resolve it. After some searching I stumbled upon a more modern slick/postgres tutorial (incidentally, this seems like a very useful blog post for anyone trying to setup a slick+postgres play app):

Below is the link to the post :

https://stonecolddev.in/posts/playing-with-scala-building-a-small-web-app-with-play-2-4-play-slick-and-postgres.html

As a result, I added these two libraries to my build.sbt:

"com.typesafe.play" %% "play-slick" % "1.1.0",
"com.typesafe.play" %% "play-slick-evolutions" % "1.1.0"

This caused my exception to change into this:

play.api.UnexpectedException: Unexpected exception[CreationException: Unable to create injector, see the following errors:

1) A binding to play.api.db.DBApi was already configured at play.api.db.slick.evolutions.EvolutionsModule.bindings(EvolutionsModule.scala:15):
Binding(interface play.api.db.DBApi to ConstructionTarget(class play.api.db.slick.evolutions.internal.DBApiAdapter) in interface javax.inject.Singleton) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1).
  at play.api.db.DBModule.bindings(DBModule.scala:25):
Binding(interface play.api.db.DBApi to ProviderConstructionTarget(class play.api.db.DBApiProvider)) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)

1 error]
    at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:165) ~[play-server_2.11-2.4.3.jar:2.4.3]
    ...

Fortunately this error is a little more common, and some quick googling confirmed my hunch - I had a library included multiple times. Google came up with:

https://groups.google.com/forum/#!topic/play-framework/NOFbfx13fio

The answer by Mirco Dotta suggests that jdbc is being including multiple times, and lo and behold, removing that from my build.sbt resolved the issue:

libraryDependencies ++= Seq(
  jdbc,  # Remove this
  cache,
  ws,
  specs2 % Test,
  "org.postgresql" % "postgresql" % "9.4-1204-jdbc4",
  "com.typesafe.play" %% "play-slick" % "1.1.0",
  "com.typesafe.play" %% "play-slick-evolutions" % "1.1.0",
  "com.typesafe.slick" %% "slick" % "3.1.0",
  "org.slf4j" % "slf4j-nop" % "1.6.4"
)

Note : jdbc was automatically put in there by activator's project generator, which made it easier to miss.

Note 2 : For anyone for whom this doesn't resolve your issue, the stonecolddev blog post also suggests he had connectivity issues which were resolved by adding the following to his application.conf, in this order:

slick.dbs.default.driver="slick.driver.PostgresDriver$"
slick.dbs.default.db.driver="org.postgresql.Driver"

Note 3 : The Hikaricp library does not need to be included, as it is built into play as of version 2.4. This is why the official docs don't refer to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM