I love bootstrapping projects with Play! Ebean, in memory database : evolutions generates automatically when I need a new model, this is awesome.
I am learning Play Scala, and there is a lack of support between Ebean and Scala
It can work, with .enablePlugins(PlayScala, PlayEbean)
But case classes are not supported as an Ebean model.
I was wondering, do you know a ORM that :
I dont think slick or JPA generates evolutions ? I tried and did not get it working.
PS : with case classes, you get implicit readers and writers from/to json and this is also awesome.
I think Slick ORM as mentioned in the answers is definitely the way to go here.
Slick ORM works great in scala, as you can use operations such as filter
, map
and other functional paradigms in this particular ORM. On another hand, slick has awesome documentation as you can see from this link:
http://slick.lightbend.com/doc/3.1.0/
Assuming that you are open to the ORM, we can easily go ahead and use slick-codegen
library that automatically reflects on your database schema and creates a file containing all the models in your database.
The documentation is here specifically on slick-codegen
:
http://slick.lightbend.com/doc/3.1.0/code-generation.html
But I'll break it down for you to make it even easier. The way to do this is as follows for postgres
:
slick-codegen
to your library dependencies by adding this line in your build.sbt : libraryDependencies += "com.typesafe.slick" %% "slick-codegen" % "3.1.0"
postgresql
driver in your build.sbtCreate the following scala file in your project (which you can right click to run in IntelliJ or may have to change it to an executable Scala file if you're not using IntelliJ. People have also figured out a way to run this via sbt itself during compile time, but I won't be getting into that) :
object SlickCodeGen {
def main(args: Array[String]): Unit = {
slick.codegen.SourceCodeGenerator.main(
Array("slick.jdbc.PostgresProfile",
"org.postgresql.Driver",
DATABASE_URL,
DIRECTORY_TO_PLACE_FILE,
PACKAGE,
USERNAME,
PASSWORD)
)
}
}
After you have run the scala file, you'll see a new file called Tables.scala
in the directory and package that you previously specified.
The file contains minimal and only necessary components for you, so for example, for a table such as Computer
that you've shown in your link, the implicit conversions from database to case classes will be generated and may look as follows(just for demo purposes, but the length of the file will be about the same, if this is too much boilerplate):
package
// AUTO-GENERATED Slick data model
/** Stand-alone Slick data model for immediate use */
object Tables extends {
val profile = slick.jdbc.PostgresProfile
} with Tables
/** Slick data model trait for extension, choice of backend or usage in the cake pattern. (Make sure to initialize this late.) */
trait Tables {
val profile: slick.jdbc.JdbcProfile
import profile.api._
import slick.model.ForeignKeyAction
// NOTE: GetResult mappers for plain SQL are only generated for tables where Slick knows how to map the types of all columns.
import slick.jdbc.{GetResult => GR}
/** DDL for all tables. Call .create to execute. */
lazy val schema
: profile.SchemaDescription = Computer.schema
@deprecated("Use .schema instead of .ddl", "3.0")
def ddl = schema
case class ComputerRow(name: String,
introduced: Date,
discontinued: Date,
company: Company)
/** GetResult implicit for fetching ComputerRow objects using plain SQL queries */
implicit def GetResultComputerRow(implicit e0: GR[String],
e1: GR[Date],
e2: GR[Company]): GR[ComputerRow] =
GR { prs =>
import prs._
ComputerRow.tupled(
(<<[String],
<<[Date],
<<[Date],
<<[Company]))
}
/** Table description of table computer. Objects of this class serve as prototypes for rows in queries. */
class Computers(_tableTag: Tag)
extends profile.api.Table[ComputerRow](_tableTag,
None,
"computer") {
def * =
(name, introduced, discontinued, company) <> (ComputerRow.tupled, ComputerRow.unapply)
/** Maps whole row to an option. Useful for outer joins. */
def ? =
(Rep.Some(name),
Rep.Some(introduced),
Rep.Some(discontinued),
Rep.Some(company).shaped.<>(
{ r =>
import r._;
_1.map(
_ =>
ComputerRow.tupled(
(_1.get, _2.get, _3.get, _4.get)))
},
(_: Any) =>
throw new Exception("Inserting into ? projection not supported.")
)
/** Database column name SqlType(text) */
val name: Rep[String] = column[String]("name", O.PrimaryKey)
/** Database column introduced SqlType(date) */
val firstName: Rep[Date] = column[Date]("introduced")
/** Database column discontinued SqlType(date) */
val lastName: Rep[Date] = column[Date]("discontinued")
/** Database column company SqlType(text) */
val gender: Rep[Company] = column[Company]("company")
}
/** Collection-like TableQuery object for table Computer */
lazy val Computer = new TableQuery(tag => new Computer(tag))
}
Computer += ComputerRow(...)
Note that ComputerRow here is a case class.
So to sum it up,
slick-codegen
can generate your scala classes automatically from the database, or the other way around by running Computer.schema
in this case.slick
is extremely scala friendly (case classes, monad operations)I think you can't go wrong with Slick here.