Runtime Schema Upgrade
Since 3.2.0, MyBatis Migrations supports runtime schema upgrade (a.k.a. in-app migration).
If you distributed your application in binary form (e.g. WAR or JAR) and want to make some changes to the database schema after the initial release, that's where the Runtime Schema Upgrade helps.
Overview
To use Runtime Schema Upgrade, you need to create Migration Scripts.
Migration scripts can be written as text files (.sql) or java classes that implements MigrationScript
interface.
Then during the startup process of your application, perform 'Up' operation by executing UpOperation#operate()
method.
Assuming that your java migration scripts are in 'mycompany.migration.script' package and you can obtain java.sql.DataSource
instance, the below is the minimum code to perform 'Up' operation.
new UpOperation().operate(
new DataSourceConnectionProvider(dataSource),
new JavaMigrationLoader("mycompany.migration.script"), null, null);
As the first migration script should be the one that creates the 'changelog' table, the initial release of your application may contain two or more migration scripts.
To upgrade the schema in the next version, just add new migration scripts to the package.
Migration Script
By default, MyBatis Migration supports two types of Migration Script: simple SQL script file (*.sql) and java migration script.
You have already learned about the simple SQL script file (*.sql) that can be generated by the migrate new
command in the former sections.
As it is just a text file, it is easy to write or edit, but a little bit harder to use with Runtime Schema Upgrade because it needs to be accessible in the file system (i.e. JAR or WAR must be extracted on the runtime environment).
A java migration script is a java class that implements MigrationScript
interface (see below).
It must be compiled, of course, but it can be placed anywhere in the classpath, so it may be a better choice for Runtime Schema Upgrade in most cases.
public interface MigrationScript {
BigDecimal getId();
String getDescription();
String getUpScript();
String getDownScript();
}
getId()
method should return a unique ID of the migration script. Newer script must return a larger number.
getDescription()
method should return a short description of the script.
getUpScript()
method should return the actual SQL statements upgrading the schema.
getDownScript()
method should return SQL statements downgrading the schema, but it exists mainly for API consistency and would not be used in Runtime Schema Upgrade.
Here is a typical implementation of the first migration script that creates the 'changelog' table:
package mycompany.migration.script
import java.math.BigDecimal;
import org.apache.ibatis.migration.MigrationScript;
public class V001_CreateChangelog implements MigrationScript {
public BigDecimal getId() {
return BigDecimal.valueOf(1L);
}
public String getDescription() {
return "Create changelog";
}
public String getUpScript() {
return "CREATE TABLE changelog ("
+ "ID NUMERIC(20,0) NOT NULL,"
+ "APPLIED_AT VARCHAR(25) NOT NULL,"
+ "DESCRIPTION VARCHAR(255) NOT NULL); "
+ "ALTER TABLE changelog "
+ "ADD CONSTRAINT PK_changelog "
+ "PRIMARY KEY (id);";
}
public String getDownScript() {
return "DROP TABLE changelog;";
}
}
UpOperation#operate()
As shown in the Overview section, Runtime Schema Upgrade is executed by calling the operate()
method of an UpOperation
instance.
The method takes four parameters.
ConnectionProvider
: Required. Explained in the later section.MigrationLoader
: Required. Explained in the later section.DatabaseOperationOption
: Optional. Ifnull
is passed, the default values are used.PrintStream
: Optional. The result of the Up operation will be output to this stream.
ConnectionProvider
ConnectionProvider
interface has only one method getConnection()
which returns java.sql.Connection
used by 'Up' operation.
public interface ConnectionProvider {
Connection getConnection() throws SQLException;
}
There are two built-in implementations: DataSourceConnectionProvider
and JdbcConnectionProvider
.
You have already seen the example usage of DataSourceConnectionProvider
in the Overview section.
The constructor takes an instance of java.sql.DataSource
as its only argument.
JdbcConnectionProvider
takes four constructor arguments: JDBC driver class, JDBC URL, username and password.
Using JdbcConnectionProvider
, the example code in the Overview section can be rewritten as follows.
new UpOperation().operate(
new JdbcConnectionProvider("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:mydb", "myname", "mypassword"),
new JavaMigrationLoader("mycompany.migration.script"), null, null);
MigrationLoader
MigrationLoader
abstracts where and how to load your migration scripts.
There are two built-in implementations: FileMigrationLoader
and JavaMigrationLoader
.
FileMigrationLoader
is used to load simple SQL scripts (*.sql).
Its constructor takes three arguments:
FileMigrationLoader(File scriptsDir, String charset, Properties properties)
scriptsDir
is the only required parameter and it indicates the directory containing the migration scripts. Note that the directory must exist in the file system of the runtime environment.charset
is the character set of the migration scripts. Ifnull
is passed, system's default charset is used.properties
is used for variable substitution when reading the migration scripts (e.g. ${changelog}).
JavaMigrationLoader
loads java classes which implement MigrationScript
interface.
There are two constructors defined for JavaMigrationLoader
JavaMigrationLoader(String... packageNames)
JavaMigrationLoader(ClassLoader classLoader, String... packageNames)
packageNames
is the list of packages that contains migration scripts. Note that the migration scripts are ordered by their IDs.classLoader
is used to search the migration scripts and is optional.