Migration is the storage schema evolution mechanism provided by Activate. Example:


class ExampleMigration extends Migration {
	def timestamp = 201208191834l
	def up = {
		table[MyEntity]
			.createTable(
				_.column[String]("attribute1"),
				_.column[Int]("attribute2"))
	}
}

There is no need to register you migration. Just put it on the project classpath and it will be found by Activate.

Important: The context definition must be declared in a base package of the migrations packages. Example: com.app.myContext for com.app.model.MyMigration

Class explanation:

def timestamp: Long
Determines the execution order of the migration. If there is more than one migration with the same timestamp, an error will be thrown.
A good pattern to this number is YYYYMMDDHHSS.

def up: Unit
Method to register the migration actions.

There is also a “down” method. It has a default implementation that reverts the migration when it’s possible. To define a custom down method, just override it.


override def down = {
	table[MyEntity]
		.removeTable
}

When the default “down” method implementation can’t revert a migration, it throws a CannotRevertMigration. Migrations actions that can’t be automatically reverted:

Remove column
Remove table
Remove nested list table
Custom script

On context startup, Activate automatically verifies if the storage schema must be updated and run pending migrations.

To override this default behavior add this code to your context:


override protected lazy val runMigrationAtStartup = false



Methods to update/revert storage migrations available at the net.fwbrasil.activate.migration.Migration object:

def update(context: ActivateContext): Unit
Updates the storage to the last available migration.

def updateTo(context: ActivateContext, timestamp: Long): Unit
Updates the storage to the specified timestamp.

def revertTo(context: ActivateContext, timestamp: Long): Unit
Reverts the storage to de specified timestamp.

Manual migration is an especial type wich indicates that the migration runs only manually and doesn’t have an automatic timestamp execution control logic. Example:


class ExampleMigration extends ManualMigration {
	def up = {
		table[MyEntity]
			.removeTable
	}
}

Methods to run manual migrations available at the net.fwbrasil.activate.migration.Migration object:

def execute(context: ActivateContext, manualMigration: ManualMigration): Unit
Executes migration up actions.

def revert(context: ActivateContext, manualMigration: ManualMigration): Unit
Executes migration down actions.

The Table class has methods to manipulate the schema. There are two methods to obtain a table object:


table("MyEntity")
table[MyEntity]

The first uses the entity name and the second the entity class. It’s recommended using the second one. The first one is designed to be used when an entity class is deleted or renamed.

Table instance method:
def createTable(definitions: ((ColumnDef) => Unit)*): CreateTable

Example including the “ifNotExists” option:


table[MyEntity]
	.createTable(
		_.column[String]("myString"),
		_.customColumn[Int]("myBigString", "CLOB"))
	.ifNotExists

Table instance method:
def removeTable: RemoveTable

Example including the “ifExists” and “cascade” options:


table[MyEntity]
	.removeTable
	.ifExists
	.cascade

Table instance method:
def renameTable(newName: String): RenameTable

Example including the “ifExists” option:


table("MyOldEntity")
	.renameTable("MyNewEntity")
	.ifExists

Table instance method:
def addColumn(definition: (ColumnDef) => Unit): AddColumn

Example including the “ifNotExists” option:


table[MyEntity]
	.addColumn(_.column[String]("myNewColumn"))
	.ifNotExists

Table instance method:
def renameColumn(oldName: String, newColumn: (ColumnDef) => Unit): RenameColumn

Example including the “ifExists” option:


table[MyEntity]
	.renameColumn("oldName", _.column[String]("newName"))
	.ifExists

Table instance method:
def renameColumn(oldName: String, newColumn: (ColumnDef) => Unit): RenameColumn

Example including the “ifExists” option:


table[MyEntity]
	.renameColumn("oldName", _.column[String]("newName"))
	.ifExists

Table instance method:
def modifyColumnType(column: (ColumnDef) => Unit): ModifyColumnType

Example including the “ifExists” option:


table[MyEntity]
	.modifyColumnType(_.column[String]("someColumn"))
	.ifExists

It is also possible to use a custom column type:


table[MyEntity]
	.modifyColumnType(_.customColumn[String]("someColumn", "TEXT"))
	.ifExists

Table instance method:
def addIndex(columnName: String, indexName: String): AddIndex

Example including the “ifNotExists” option:


table[MyEntity]
	.addIndex("columnName", "indexName")
	.ifNotExists

Table instance method:
def removeIndex(columnName: String, indexName: String): RemoveIndex

Example including the “ifExists” option:


table[MyEntity]
	.removeIndex("columnName", "indexName")
	.ifExists

Table instance method:
def addReference(columnName: String, referencedTable: Table, constraintName: String): AddReference

Example including the “ifNotExists” option:


table[MyEntity]
	.addReference("columnName", table[ReferencedEntity], "constraintName")
	.ifNotExists

Table instance method:
def removeReference(columnName: String, referencedTable: Table, constraintName: String): RemoveReference

Example including the “ifExists” option:


table[MyEntity]
	.removeReference("columnName", table[ReferencedEntity], "constraintName")
	.ifNotExists

Table instance method:
def createNestedListTableOf[T](listName: String, listTableName : String): IfNotExistsBag

Creates a nested table to persist entity list properties. The “T” generic is the list content type.


table[MyEntity]
	.createNestedListTableOf[Int]("intList", "listTableName")
	.ifNotExists

Mongo and memory storages persists simple “native” nested lists, ignoring this action. On jdbc this method perform this actions:

- Creates a column with the list name in the entity table. This column is used to indicate if the property is null (0) or not null (1).
- Creates a table with the listTableName. It contains two columns: “owner”, referencing the entity table, and “value”, containing the list item value.
- Creates an index on the owner column

Activate automatically manages the list content using this jdbc storage structures when the entity is created, modified or deleted.

Table instance method:
def removeNestedListTable(listName: String): IfExistsBag

Removes a nested list table.


table[MyEntity]
	.removeNestedListTable("intList")
	.ifExists

A custom script is an arbitrary code block that runs as a migration action inside a transaction. Example:


customScript {
	all[MyEntity].foreach(_.doSomething)
}

Using mass update/delete:


customScript {
	update {
		(entity: MyEntity) => where(entity.name :== "someName") set(entity.name := "someName2")
	}
	delete {
		(entity: MyEntity) => where(entity.name :== "someName2")
	}
}

It’s possible to use the direct access to the storage. Be aware that the direct access turns your software storage dependent. Example with a jdbc storage:


customScript {
	val connection = storage.directAccess
	try {
		connection
			.prepareStatement("update MyEntity set attribute1 = 'a'")
			.executeUpdate
		connection.commit
	} catch {
		case e =>
			connection.rollback
			throw e
	} finally
		connection.close
}

Activate determines the column type based on the attribute class. It’s possible to determine a custom column type. In the places that you use:


_.column[String]("attribute1")

Just modify to:


_.customColumn[String]("attribute1", "CLOB")

The second parameter is the database type name. Be aware that this makes your code storage dependent.

There are some auxiliary methods that can be used to modify the schema based on your entities classes automatically. Use these methods carefully. Otherwise, your migrations can be incompatible if you have to migrate a new database from zero.

They are self explanatory:


createTableForAllEntities
	.ifNotExists
createTableForEntity[MyEntity]
	.ifNotExists

removeAllEntitiesTables
	.ifExists
	.cascade

createInexistentColumnsForAllEntities
createInexistentColumnsForEntity[MyEntity]

createReferencesForAllEntities
	.ifNotExists
createReferencesForEntity[MyEntity]
	.ifNotExists

removeReferencesForAllEntities
	.ifExists