(This question is based on a very similar previous request for help. With the introduction of a DAO and multiple database drivers, the same problem requires a different approach, and I hope warrants a new SO question.)
I have a class
and Slick Table
defined like this:
import play.api.db.slick.Profile
case class Foo(title: String, description: String, id: Int = 0)
trait FooComponent extends Profile { this: Profile =>
import profile.simple._
class FooTable(tag: Tag) extends Table[Foo](tag, "FOO") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def title = column[String]("TITLE", O.NotNull)
def description = column[String]("DESCRIPTION")
def * = (title, description, id) <> (Foo.tupled, Foo.unapply)
}
}
And a data access object:
class DAO(override val profile: JdbcProfile) extends FooComponent with Profile {
val foos = TableQuery[FooTable]
}
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
This is pretty awesome, because now I can add something like the following to my application.conf
:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.test.driver=org.postgresql.Driver
db.test.user="testuser"
db.test.password=""
db.test.url="jdbc:postgresql:testdb"
... and if I do the following in a Controller:
import models.current.dao._
import models.current.dao.profile.simple._
I have access to my foos
TableQuery
, and it automagically gets the driver and database url given for db.default
in application.conf
.
In a similar, but not-quite-as-nice way, I can do the following in my test Specification
:
"test Foos" in new WithApplication() {
val dao = new DAO(play.api.db.slick.DB("test").driver)
import dao._ //import all our database Tables
import dao.profile.simple._ //import specific database methods
play.api.db.slick.DB("test").withSession { implicit s: Session =>
println(s.conn.getMetaData.getURL)
println(foos.list)
}
However, what if I want to define a method which can act on a TableQuery[Foo]
? Something like this:
def findByTitle(title: String) = foos.filter(_.id === id).list
Problem
What's the correct way of writing the findByTitle
method, and where should I put it so that I can:
- Call it in a way such that it won't collide with a method of the same name which acts on
TableQuery[Bar]
. Coming from OO, I feel like I want to do something likefoos.findByTitle("someFoo")
, but if there's a better way of doing this functional-style, I'm open to suggestions. - Call it from an application Controller such that the query will work with my
db.default
h2 driver, and from my testSpecification
so that it will work with mydb.test
postgres driver.
As an aside, if I can put this in my DAO:
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
and then import models.dao.current._
anywhere I want to use this DAO, how can I extend the same form to the following:
object test {
val dao = new DAO(play.api.db.slick.DB("test").driver)
}
If I try to do this, the compiler complains about not having an implicit Application in scope
.
I think you need to read up in implicit conversion and implicit parameters in Scala. There are online Scala books available.
When you get an error message about a missing implicit it either means you ran into a failing type-check provided by a library preventing you from doing something wrong, but that's not the case here. Or you simply forgot to make the implicit available. There are two ways to make an implicit available. Either import it into the scope where you get the error message. Or basically defer the lookup to the callsite of your method. Not sure which one is the right one for play. You either need to import the implicit Application from play, or you need turn
val dao
into a method and request an implicit application in an implicit argument listdef dao(implicit app: Application) = ...
. You can alternatively turn test into a class and request it there.If you use the play slick plugin it will need a started play application to be able to call code that uses the DB access from that plugin, you can make sure to start a play app in your tests using
WithApplication
as described in the docs: http://www.playframework.com/documentation/2.3.x/ScalaFunctionalTestingWithSpecs2