I am using play-slick with slick evolutions to create my db.
I have the following config
slick.dbs.default.profile="slick.jdbc.H2Profile$"
slick.dbs.default.db.driver="org.h2.Driver"
slick.dbs.default.db.url="jdbc:h2:mem:play;DB_CLOSE_DELAY=-1"
This works fine when I want to use slick itself to operate on the db. However, I want to access the underlying javax.sql.Datasource
instance (which I need to pass in to another library, namely pac4js DbProfileService
).
I tried to add a config for the db name as dbName="play"
, and then add
@Provides
def dbProfileService(dbApi: DBApi): DbProfileService = {
new DbProfileService(dbApi.database(configuration.get[String]("dbName")).dataSource)
}
however, it seems that the injection happens before the evolution script has a chance to run, which causes an exception saying that the play
database isn't able to be found.
Is there a better approach for this problem?
Here is the stack trace
aused by: java.lang.IllegalArgumentException: Could not find database for play
at play.api.db.slick.evolutions.internal.DBApiAdapter.$anonfun$database$1(DBApiAdapter.scala:32)
at scala.collection.immutable.Map$Map1.getOrElse(Map.scala:248)
at play.api.db.slick.evolutions.internal.DBApiAdapter.database(DBApiAdapter.scala:32)
at modules.SecurityModule.dbProfileService(SecurityModule.scala:53)
at modules.SecurityModule$$FastClassByGuice$$153731.GUICE$TRAMPOLINE(<generated>)
at modules.SecurityModule$$FastClassByGuice$$153731.apply(<generated>)
at com.google.inject.internal.ProviderMethod$FastClassProviderMethod.doProvision(ProviderMethod.java:260)
at com.google.inject.internal.ProviderMethod.doProvision(ProviderMethod.java:171)
at com.google.inject.internal.InternalProviderInstanceBindingImpl$CyclicFactory.provision(InternalProviderInstanceBindingImpl.java:185)
at com.google.inject.internal.InternalProviderInstanceBindingImpl$CyclicFactory.get(InternalProviderInstanceBindingImpl.java:162)
at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40)
at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60)
at com.google.inject.internal.ProviderMethod.doProvision(ProviderMethod.java:171)
at com.google.inject.internal.InternalProviderInstanceBindingImpl$CyclicFactory.provision(InternalProviderInstanceBindingImpl.java:185)
at com.google.inject.internal.InternalProviderInstanceBindingImpl$CyclicFactory.get(InternalProviderInstanceBindingImpl.java:162)
at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:40)
at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:60)
at com.google.inject.internal.ProviderMethod.doProvision(ProviderMethod.java:171)
at com.google.inject.internal.InternalProviderInstanceBindingImpl$CyclicFactory.provision(InternalProviderInstanceBindingImpl.java:185)
at com.google.inject.internal.InternalProviderInstanceBindingImpl$CyclicFactory.get(InternalProviderInstanceBindingImpl.java:162)
at com.google.inject.internal.SingleFieldInjector.inject(SingleFieldInjector.java:50)
at com.google.inject.internal.MembersInjectorImpl.injectMembers(MembersInjectorImpl.java:146)
at com.google.inject.internal.MembersInjectorImpl.injectAndNotify(MembersInjectorImpl.java:101)
at com.google.inject.internal.Initializer$InjectableReference.get(Initializer.java:245)
at com.google.inject.internal.Initializer.injectAll(Initializer.java:140)
at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:180)
I also added a breakpoint, and sure enough, there was only the default
db there, not the play
db as I would expect.
The problem is with the value of the key dbName
. When you are doing
configuration.get[String]("dbName")
You are looking up for the value of the key named dbName
that is in your config files. In the provided example, the value is play
. Now, looking at the complete line you have this
@Provides
def dbProfileService(dbApi: DBApi): DbProfileService = {
new DbProfileService(
dbApi // trait play.api.db.DBApi
.database( // method from `DBApi`
configuration.get[String]("dbName") // getting the value of the key `dbName` from config file
)
.dataSource
)
}
You are calling the database
method from DBApi
. There are two classes that are mixing this trait. One is DBApiAdapter from play-slick-evolutions
and the other one is DefaultDBApi from play-jdbc
. I think in your case is using the one that belongs to the evolution module. If you look at the implementaiton of the method database
that you are calling, both classes have the same logic (so, it doesn't matter which implementation is being used).
def database(name: String): Database = {
databaseByName.getOrElse(name, throw new IllegalArgumentException(s"Could not find database for $name"))
}
This means that it will look for a key with the prefix slick.dbs.<db name>
. Based on the error message you got
caused by: java.lang.IllegalArgumentException: Could not find database for play
at play.api.db.slick.evolutions.internal.DBApiAdapter.$anonfun$database$1(DBApiAdapter.scala:32)
you tried to look for a db config with the prefix slick.dbs.play
and the stracktrace shows that in this case the implementation from evolutino module is being used.
To solve the issue you have two options:
play
of the key dbName
to default
slick.dbs.default
to slick.dbs.play