A typical Scala-Forklift project is consist of several sub-projects, as shown in this tutorial. They are:
app
is where your application code resides.migrations
stores all the migration scripts, and the tools to preview and apply migrations.migration_manager
is where customized migration manager is defined. There is also a tool to run some rescue commands, in case your code inmigrations
cannot be run or compiled.generated_code
is where the code generated by Slick Code Generator is stored. You should NOT modify or edit code in this sub-project.tools
is to provide other convenient tools. For now, there's only a tool to help users work with git.
Project migrations
contains the migration scripts and tools to apply these scripts. Simply launch sbt
shell and enter:
migrations/run
This will gives you a list of available commands. Since this command could be used a lot during development, we have defined a shortcut in build.sbt
:
addCommandAlias("mg", "migrations/run")
So you can enter the following command in your sbt
shell:
mg init
This is equivalent to migrations/run init
. This will make your database ready for migrations.
Your migration scripts are stored in directory migrations/src_migrations/main/scala
, with the file named by numbers. For example, 1.scala
creates the table users
:
object M1 {
MyMigrations.migrations = MyMigrations.migrations :+ SqlMigration(1)(List(
sqlu"""create table "users" ("id" INTEGER NOT NULL PRIMARY KEY,"first" VARCHAR NOT NULL,"last" VARCHAR NOT NULL)"""
))
}
Both SQL queries and Slick statements can be used in a migration. Details will be discussed in the next section. For now, let's just apply these migrations by entering the following command in your sbt
shell:
~mg migrate
This command will find all the not yet applied migrations, and try to apply them to your database. You should be able to see the following 3 migrations applied.
1 SqlMigration:
[create table "users" ("id" INTEGER NOT NULL PRIMARY KEY,"first" VARCHAR NOT NULL,"last" VARCHAR NOT NULL)]
2 DBIOMigration:
slick.driver.H2Driver.api.queryInsertActionExtensionMethods[datamodel.v1.schema.tables.Users#TableElementType, Seq](Users).++=(collection.Seq[datamodel.v1.schema.tables.UsersRow](UsersRow(1, "Chris", "Vogt"), UsersRow(2, "Stefan", "Zeiger")))
3 SqlMigration:
[alter table "users" alter column "first" rename to "firstname" ]
[alter table "users" alter column "last" rename to "lastname" ]
Alternatively, you can also use ~mg migrate -p
to have Scala-Forklift ask for your confirmation before actually applying the migrations.
Congratulations! You have applied all the migration scripts. To see what's in current database, uncomment the code in app/src/main/scala/App.scala
and enter the following command in your sbt
shell:
app/run
You should be able to see something similar to this in the output:
Users in the database:
UsersRow(1,Chris,Vogt)
UsersRow(2,Yao,Li)
The migrations are stored in the src_migrations
directory in the migrations
sub-project. You can use mg new s
(for SQL migrations) or mg new d
(for Slick migrations) to create a new migration file.
To apply these migrations, you must first copy them to the src
directory. Entering mg update
in your sbt
shell will first unapplied migration files to the src
directory. Then you can use mg preview
to preview the migrations going to be applied, and mg apply
to actually apply them.
The applied migrations could change the database schemas. In Scala-Forklift, it is recommended to use the source code generator (which uses the Slick source code generator) to generate the Scala model for you. Command mg codegen
will generate the models for the latest database. The generated code resides in the generated_code
sub-project.
However, using these commands in order for every migration could be unpleasant. Scala-Forklift provides a shortcut command ~mg migrate
, which will run update
, preview
, apply
, and codegen
subsequently for you. In practice, it is recommended that you ALWAYS use ~mg migrate
.
A migration can be written in plain SQL or Slick queries.
You can use mg new s
to create a new SQL migration in your src_migrations
directory.
By using the sqlu
interpolator in Slick you can write plain SQL queries in your migrations, as shown in 1.scala
:
object M1 {
MyMigrations.migrations = MyMigrations.migrations :+ SqlMigration(1)(List(
sqlu"""create table "users" ("id" INTEGER NOT NULL PRIMARY KEY,"first" VARCHAR NOT NULL,"last" VARCHAR NOT NULL)"""
))
}
Comparing with plain SQL queries, Slick queries are type-safe.
You can use mg new d
to create a new Slick migration in your src_migrations
directory.
A Slick migration can use the models generated by a previous migration. By convention, you should ALWAYS use code generated for the previous migration. For example, use datamodel.v1.schema.tables.Users
in your 2.scala
:
import slick.driver.H2Driver.api._
import com.liyaos.forklift.slick.DBIOMigration
import datamodel.v1.schema.tables.Users
import datamodel.v1.schema.tables.UsersRow
object M2 {
MyMigrations.migrations = MyMigrations.migrations :+ DBIOMigration(2)(
DBIO.seq(Users ++= Seq(
UsersRow(1, "Chris","Vogt"),
UsersRow(2, "Yao","Li")
)))
}
In project migration_manager
, you can define your own migration manager and code generator.
Scala-Forklift provides a SlickMigrationManager
to manage your slick migration scripts. To use SlickMigrationManager
, add the following library dependency to your build.sbt
in this project.
libraryDependencies += "com.liyaos" %% "scala-forklift-slick" % "0.2.3"
Then you can override original methods or define new methods for your migration manager here:
import com.liyaos.forklift.core.Migration
import com.liyaos.forklift.slick.SlickMigrationManager
trait MyMigrationManager extends SlickMigrationManager {
// define your methods or override original methods here
}
Scala-Forklift for Slick provides a wrapper for Slick source code generator. You can also customize it in your migration_manager
sub-project:
trait MyCodegen extends SlickCodegen {
// change directory of generated file options here
// override val generatedDir = ...
// override val container = ...
// override val fileName = ...
}
Scala-Forklift provides a git tool to help you manage your dev db with git. Check here for a detailed explanation.
Your application code in app
sub-project should look exactly like your other application code does not use Scala-Forklift, except that it does not contain the Scala code for your database models, but depends on the code generated by a source code generator (which is also a good practice when using Slick). Your application should depend on the generated_code
sub-project, and import these models from datamodel.latest.schema.tables
.
object v* is not a member of package datamodel
, ornot found: object datamodel
: runmgm rescue
to delete the generated files and unlink the migration files insrc
, and then runmg codegen
to generate code for current database before entering~mg migrate
.