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
:
- Add
slick-codegen
to your library dependencies by adding this line in your build.sbt : libraryDependencies += "com.typesafe.slick" %% "slick-codegen" % "3.1.0"
- Make sure your database is running on whatever port (let's say 5432) for postgres and include the appropriate
postgresql
driver in your build.sbt
Create 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))
}
- Once this file is created, then you can easily leverage the use of case classes (like you wanted), one example is when you need to add a new row to the computer table, you can simply do
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)
- decent boilerplate but really use to use in your application, also can be customized and all the tables can be created in one file or separated depending on your needs.
I think you can't go wrong with Slick here.