How does slick profile work with respect to slick DB - scala

I am having trouble to understand how to use slick profile.
My problem:
I am trying to use Slick with Akka-stream via the Alpakka JDBC plugin. The example given online is as follows:
#Load using SlickSession.forConfig("slick-h2")
slick-h2 {
profile = "slick.jdbc.H2Profile$"
db {
connectionPool = disabled
dataSourceClass = "slick.jdbc.DriverDataSource"
properties = {
driver = "org.h2.Driver"
url = "jdbc:h2:/tmp/alpakka-slick-h2-test"
}
}
}
import scala.concurrent.Future
import akka.Done
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.stream.alpakka.slick.scaladsl._
import slick.jdbc.GetResult
object SlickSourceWithPlainSQLQueryExample extends App {
implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
implicit val ec = system.dispatcher
implicit val session = SlickSession.forConfig("slick-h2")
// The example domain
case class User(id: Int, name: String)
// We need this to automatically transform result rows
// into instances of the User class.
// Please import slick.jdbc.GetResult
// See also: "http://slick.lightbend.com/doc/3.2.1/sql.html#result-sets"
implicit val getUserResult = GetResult(r => User(r.nextInt, r.nextString))
// This import enables the use of the Slick sql"...",
// sqlu"...", and sqlt"..." String interpolators.
// See also: "http://slick.lightbend.com/doc/3.2.1/sql.html#string-interpolation"
import session.profile.api._
// Stream the results of a query
val done: Future[Done] =
Slick
.source(sql"SELECT ID, NAME FROM ALPAKKA_SLICK_SCALADSL_TEST_USERS".as[User])
.log("user")
.runWith(Sink.ignore)
done.onComplete {
case _ =>
session.close()
system.terminate()
}
}
The issue is that it works with
implicit val session = SlickSession.forConfig("slick-h2")
I try to use slick session as follows:
object Main extends App {
implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
implicit val ec = system.dispatcher
implicit val session = SlickSession.forConfig("pp")
}
Where my pp config is as such:
pp = {
url = "jdbc:oracle:thin:#52.4.90.244:1521:pp"
driver = oracle.jdbc.OracleDriver
keepAliveConnection = true
connectionPool = disabled
user = "xxxxx"
password = "xxxxx"
}
This code breaks at runtime.
Exception in thread "main" slick.SlickException: Configured profile oracle.jdbc.OracleDriver does not conform to requested profile slick.jdbc.JdbcProfile
at slick.basic.DatabaseConfig$.forConfig(DatabaseConfig.scala:99)
at akka.stream.alpakka.slick.javadsl.SlickSession$.forConfig(package.scala:47)
at akka.stream.alpakka.slick.javadsl.SlickSession$.forConfig(package.scala:44)
However in another code, where I do not use Akka-Stream and therefore do not use slickSession as such
object Main extends App {
val db = Database.forConfig("pp")
.....}
the code works perfectly.
I concluded that this has to do with Database.forconfig("pp") and SlickSession.forConfig("slick-h2") that require 2 different things.
There is an explanation about profile in Slick website but that is not very easy to understand and provides little instruction. It does not list the available profile and their syntax.
Hence my question is, what's the difference between the two forConfig.
How the profile works, where are they needed. Why the two configuration files for the database are not dealt with the same way.
Finally and formost, what is the profile for oracle. In slick 3.2.3 Slick is now free. I could not find the profile for it as in profile = "slick.jdbc.H2Profile$"
Can someone help clarify the configuration file difference, what is expected, what the profile does, and what is the profile for Oracle ?

The upgrade guide in the Slick documentation explains the distinction between drivers and profiles:
Slick’s driver concept has been renamed to profile to end the confusion over Slick drivers vs JDBC drivers....
As for the reason that Alpakka's SlickSession doesn't accept your configuration, take a look at the source code:
object SlickSession {
private final class SlickSessionImpl(val slick: DatabaseConfig[JdbcProfile]) extends SlickSession {
val db: JdbcBackend#Database = slick.db
val profile: JdbcProfile = slick.profile // <-- expects a profile
}
...
}
SlickSession specifically looks for a profile in the configuration, which is why defining a driver won't work.
To define a profile for Oracle, use OracleProfile.

As a follow up of #Jeffrey Chung answer. Here is the solution.
implicit val session = SlickSession.forConfig("pp")
import session.profile.api._
and the configuration file should be as such:
pp {
profile = "slick.jdbc.OracleProfile$"
db {
url = "..."
driver = oracle.jdbc.OracleDriver
keepAliveConnection = true
connectionPool = disabled
user = "...."
password = "...."
}
}

Related

Testing Play + Slick app

I've a simple CRUD app built with Scala Play 2.4.3 and Play-slick 1.1.0 (slick 3.1.0) that uses a MySQL database for persistent storage.
I was trying to create the tests for my app and I saw 2 main options:
mocking database access, that as far as I've seen, requires some code changes
make tests use an alternative database (probably, in memory H2).
What's the best approach (vantages and desavantages)?
I prefer the second approach, but I'm finding some difficulties in setting up the tests.
What do I need to do? First, I think that I need to do the tests run with a FakeApplication, right? Do I need any sbt dependency to be able to do that?
After that, how do I specify to use the H2 database?
I had the same struggle and I came up with a solution like this(using second approach):
Create a context for DAO to use:
trait BaseContext{
def dbName: String
val dbConfig = DatabaseConfigProvider.get[JdbcProfile](dbName)
val db = dbConfig.db
val profile = dbConfig.driver
val tables = new Tables { // this is generated by Schema Code Generator
override val profile: JdbcProfile = dbConfig.driver
}
}
#Singleton
class AppContext extends BaseContext{
def dbName = "mysql" // name in your conf right after "slick.dbs"
}
#Singleton
class TestingContext extends BaseContext{
def dbName = "h2"
}
Then create a module to bind the injection, and don't forget to enable it in conf using play.modules.enabled += "your.Module":
class ContextModule(environment: Environment, configuration: Configuration) extends AbstractModule {
override def configure(): Unit = {
if (configuration.getString("app.mode").contains("test")) {
bind(classOf[BaseContext])
.to(classOf[TestingContext])
} else {
bind(classOf[BaseContext])
.to(classOf[AppContext])
}
}
}
And inject it to every DAO you've created:
class SomeDAO #Inject()(context: BaseContext){
val dbConfig = context.dbConfig
val db = context.db
val tables = context.tables
import tables.profile.api._
def otherStuff....
// you can call db.run(...), tables.WhateverYourTableIs, tables.TableRowCaseClass, ...
}
And final step, your configuration file. In my case I used app.mode to mark the environment, and I use separate .conf for different environment. Of cause, in these conf you must have the correct DB configuration. Here's the sample:
app.mode = "test"
# Database configuration
slick.dbs = {
# for unit test
h2 {
driver = "slick.driver.H2Driver$"
db = {
url = "jdbc:h2:mem:test;MODE=MYSQL"
driver = "org.h2.Driver"
keepAliveConnection = true
}
}
}
I'm pretty sure my solution is not a elegant one, but it deliver the goods. :)
Any better solution is welcomed!
my solution was to add step(Play.start(fakeApp)) in the beginning of each spec, and step(Play.stop(fakeApp)) in the end of each spec.
Also:
def fakeApp: FakeApplication = {
FakeApplication(additionalConfiguration =
Map(
"slick.dbs.default.driver" -> "slick.driver.H2Driver$",
"slick.dbs.default.db.driver" -> "org.h2.Driver",
"slick.dbs.default.db.url" -> "jdbc:h2:mem:play"
))
}
This was needed because I'm using play-slick, which requires configurations like:
slick.dbs.default.driver = "slick.driver.MySQLDriver$"
slick.dbs.default.db.driver = "com.mysql.jdbc.Driver"
slick.dbs.default.db.url = "jdbc:mysql://localhost/database"
slick.dbs.default.db.user = "user"
slick.dbs.default.db.password = "password"
more info on the docs

How to apply manually evolutions in tests with Slick and Play! 2.4

I would like to manually run my evolution script at the beginning of each test file. I'm working with Play! 2.4 and Slick 3.
According to the documentation, the way to go seems to be:
Evolutions.applyEvolutions(database)
but I don't manage to get an instance of my database. In the documentation play.api.db.Databases is imported in order to get a database instance but if I try to import it, I get this error: object Databases is not a member of package play.api.db
How can I get an instance of my database in order to run the evolution script?
Edit: as asked in the comments, here is the entire source code giving the error:
import models._
import org.scalatest.concurrent.ScalaFutures._
import org.scalatest.time.{Seconds, Span}
import org.scalatestplus.play._
import play.api.db.evolutions.Evolutions
import play.api.db.Databases
class TestAddressModel extends PlaySpec with OneAppPerSuite {
lazy val appBuilder = new GuiceApplicationBuilder()
lazy val injector = appBuilder.injector()
lazy val dbConfProvider = injector.instanceOf[DatabaseConfigProvider]
def beforeAll() = {
//val database: Database = ???
//Evolutions.applyEvolutions(database)
}
"test" must {
"test" in { }
}
}
I finally found this solution. I inject with Guice:
lazy val appBuilder = new GuiceApplicationBuilder()
lazy val injector = appBuilder.injector()
lazy val databaseApi = injector.instanceOf[DBApi] //here is the important line
(You have to import play.api.db.DBApi.)
And in my tests, I simply do the following (actually I use an other database for my tests):
override def beforeAll() = {
Evolutions.applyEvolutions(databaseApi.database("default"))
}
override def afterAll() = {
Evolutions.cleanupEvolutions(databaseApi.database("default"))
}
Considering that you are using Play 2.4, where evolutions were moved into a separate module, you have to add evolutions to your project dependencies.
libraryDependencies += evolutions
Source: Evolutions
Relevant commit: Split play-jdbc into three different modules
To have access to play.api.db.Databases, you must add jdbc to your dependencies :
libraryDependencies += jdbc
Hope it helps some people passing here.
EDIT: the code would then look like this :
import play.api.db.Databases
val database = Databases(
driver = "com.mysql.jdbc.Driver",
url = "jdbc:mysql://localhost/test",
name = "mydatabase",
config = Map(
"user" -> "test",
"password" -> "secret"
)
)
You now have an instance of the DB, and can execute queries on it :
val statement = database.getConnection().createStatement()
val resultSet = statement.executeQuery("some_sql_query")
You can see more from the docs
EDIT: typo
I find the easiest way to run tests with evolutions applied is to use FakeApplication, and input the connection info for the DB manually.
def withDB[T](code: => T): T =
// Create application to run database evolutions
running(FakeApplication(additionalConfiguration = Map(
"db.default.driver" -> "<my-driver-class>",
"db.default.url" -> "<my-db-url>",
"db.default.user" -> "<my-db>",
"db.default.password" -> "<my-password>",
"evolutionplugin" -> "enabled"
))) {
// Start a db session
withSession(code)
}
Use it like this:
"test" in withDB { }
This allows you, for example, to use an in-memory database for speeding up your unit tests.
You can access the DB instance as play.api.db.DB if you need it. You'll also need to import play.api.Play.current.
Use FakeApplication to read your DB configuration and provide a DB instance.
def withDB[T](code: => T): T =
// Create application to run database evolutions
running(FakeApplication(additionalConfiguration = Map(
"evolutionplugin" -> "disabled"))) {
import play.api.Play.current
val database = play.api.db.DB
Evolutions.applyEvolutions(database)
withSession(code)
Evolutions.cleanupEvolutions(database)
}
Use it like this:
"test" in withDB { }

Best Practise of Using Connection Pool in Slick 3.0.0 Together with Play Framework

I followed the documentation of Slick 3.0.0-RC1, using Typesafe Config as database connection configuration. Here is my conf:
database = {
driver = "org.postgresql.Driver"
url = "jdbc:postgresql://localhost:5432/postgre"
user = "postgre"
}
I established a file Locale.scala as:
package models
import slick.driver.PostgresDriver.api._
import scala.concurrent.Future
case class Locale(id: String, name: String)
class Locales(tag: Tag) extends Table[Locale](tag, "LOCALES") {
def id = column[String]("ID", O.PrimaryKey)
def name = column[String]("NAME")
def * = (id, name) <> (Locale.tupled, Locale.unapply)
}
object Locales {
private val locales = TableQuery[Locales]
val db = Database.forConfig("database")
def count: Future[Int] =
try db.run(locales.length.result)
finally db.close
}
Then I got confused that when and where the proper time is to create Database object using
val db = Database.forConfig("database")
If I create db like this, there will be as many Database objects as my models. So what is the best practice to get this work?
You can create an Object DBLocator and load it using lazy operator so that its loaded only on demand.
You can always invoke the method defined in DBLocator class to get an instance of Session.

Getting Swagger to work in Play2 Scala

I'm attempting to get Swagger working in a Play2 application. I've include the dependancy in build.sbt. "com.wordnik" %% "swagger-play2" % "1.3.1"
I've defined a series of routes.
GET /api-docs controllers.ApiHelpController.getResources
GET /api-docs/building controllers.ApiHelpController.getResource(path = "/building")
GET /building controllers.Application.building
I also have a model that pulls data from Slick and has annotations.
package models
import scala.slick.driver.SQLServerDriver.simple._
import play.api.libs.json._
import play.api.db.DB
import play.api.Play.current
import Database.threadLocalSession
#XmlRootElement(name = "Building")
case class Building(#XmlElement(name = "BuildingCode") BuildingCode: String,
#XmlElement(name = "BuildingDescription") BuildingDescription: String)
object Building {
lazy val database = Database.forDataSource(DB.getDataSource())
implicit val BuildingReads = Json.reads[Building]
implicit val BuildingWrites = Json.writes[Building]
val BuildingTable = new Table[Building]("building"){
def BuildingCode = column[String]("BuildingCode", O.PrimaryKey)
def BuildingDescription = column[String]("BuildingDescription")
def * = BuildingCode ~ BuildingDescription <> (Building.apply _, Building.unapply _)
}
def getAll: Seq[Building] = {
database withSession {
val q = Query(BuildingTable)
q.list
}
}
}
Here is what my controller looks like.
object Application extends Controller {
def building = Action {
Ok(Json.toJson(Building.getAll))
}
}
When I navigate to /api-docs the page renders.
{"apiVersion":"beta","swaggerVersion":"1.2"}
Then if I navigate to /api-docs/building nothing is rendered. Finally when I navigate to /building I receive the payload. I'm not sure what else I have to do for Swagger to generate it's data. Can someone point me in the right direction?
Can you post your controller implementation? The error is most likely due to missing/improper annotations.
Also, check out https://github.com/wordnik/swagger-core/tree/master/samples/scala-play2 for a good sample play2-swagger app.

Scala, Play Framework Slick issue - could not find implicit value for parameter rconv

I'm following guidelines of Slick documentation and I don't understand what I'm doing wrong here:
package models
import scala.slick.session.Database
import Database.threadLocalSession
import scala.slick.jdbc.{GetResult, StaticQuery => Q}
import javax.sql.DataSource
import Q.interpolation
object Data {
case class User(user: String, password: String)
lazy val db = Database.forName("default")
def result: Option[User] = {
db.withSession {
sql"SELECT user, password FROM user WHERE user = 'user' AND password = 'pass'".as[User]
}
}
}
The line
sql"SELECT user, password FROM user WHERE user = 'user' AND password = 'pass'".as[User]
is giving me this:
Multiple markers at this line
- could not find implicit value for parameter rconv: scala.slick.jdbc.GetResult[models.Data.User]
- could not find implicit value for parameter rconv: scala.slick.jdbc.GetResult[models.Data.User]
What am I doing wrong here?
Play Framework 2.2.0, Scala 2.10.3, Slick 1.0.1
You need to provide the conversion function from result to user. Copied and adapted straight from the slick home:
implicit val getUserResult = GetResult(r => User(r.<<, r.<<))
Or this section from the documentation you linked