Migration Hooks
Since 3.3.0, MyBatis Migrations supports pre/post-migration hooks.
Overview
You can write scripts that are executed before or after up/down operation.
Hook scripts can be written in SQL or JSR-223 compliannt script languages.
Quick start
Here is what you need to do to use hook scripts.
- Create hooks directory.
- Create a hook script in hooks directory.
- Add a setting to the environment properties file.
1. Create hooks directory.
Create a directory named hooks in the base directory.
2. Create a hook script in hooks directory.
The following script outputs the famous string to the log.
Save this script in the hooks directory as hello.js.
print('Hello, World!');
3. Add a setting to the environment properties file.
To configure Migrations, add the following line to the development.properties in the environments directory.
hook_before_up=JavaScript:hello.js
The details will be explained in the later section, but the above line tells Migrations to execute hello.js at the beginning of migrate up
operation.
Now, if you run migrate up
, you will find the following lines in the log.
========== Applying JSR-223 hook : hello.js ==========
Hello, World!
NOTE The hook script will not be executed if there was no pending migration.
Configuration
As shown in the Quick Start section, migration hook is configured by adding a line in the environment properties file in key=value
format.
Keys for available hooks
Here is the list of available hooks.
- hook_before_up
- hook_before_each_up
- hook_after_each_up
- hook_after_up
- hook_before_down
- hook_before_each_down
- hook_after_each_down
- hook_after_down
- hook_before_new [1] (since 3.3.5)
- hook_after_new [1] (since 3.3.5)
- hook_before_script [1] (since 3.3.10)
- hook_before_each_script [1] (since 3.3.10)
- hook_after_each_script [1] (since 3.3.10)
- hook_after_script [1] (since 3.3.10)
[1] Only JSR-223 script is supported.
Minimum setting : language and file name
The value part of the setting line consists of two or more segments separated with a colon :
.
The first segment is the language name (e.g. SQL
, JavaScript
, Groovy
, etc.). The second segment is the file name. These two segments are required.
Here are some examples:
hook_before_up=SQL:insert_log.sql
hook_after_up=JavaScript:RestartServer.js
Constant variables
The other segments are used to define constant variables specific to this particular hook script. These variables can have arbitrary names, but there also are some special variable names for JSR-223 hooks that are explained in the later section.
The following settings reference the same hook script printvar.js with different variable values.
// development.properties
hook_before_up=JavaScript:printvar.js:when=before:what=up
hook_after_down=JavaScript:printvar.js:when=after:what=down
Constant variables can be referenced as global variables in JavaScript.
// printvar.js
print('This is ' + when + ' ' + what + ' hook.');
The above script will print This is before up hook.
on migrate up
and This is after down hook.
on migrate down
.
Also, if there are global variables defined in the environment properties file, they can be used in hook scripts in the same manner.
// development.properties
foo=bar
These variables can be used in SQL hook scripts as well.
# development.properties
hook_before_up=SQL:update_timestamp.sql:col=before
hook_after_up=SQL:update_timestamp.sql:col=after
// update_timestamp.sql
update worklog set ${col} = current_date();
Advanced usage of JSR-223 scripts
Get paths to the directories
An instance of SelectedPaths object is accessible as a global variable migrationPaths
.
print(migrationPaths.getBasePath());
print(migrationPaths.getEnvPath());
print(migrationPaths.getScriptPath());
print(migrationPaths.getDriverPath());
print(migrationPaths.getHookPath());
About hookContext
A global variable hookContext
is passed to each hook script, but the object bound to this variable depends on the hook.
- For
up
anddown
hooks,hookConte
is an instance of HookContext. - For
new
hooks,hookContext
is an instance of NewHookContext. - For
script
hooks,hookContext
is an instance of ScriptHookContext.
Accessing Change object (in each hook only)
In an each hook script, an instance of Change object is accessible via the global variable hookContext
.
print(hookContext.getChange().getId());
print(hookContext.getChange().getFilename());
NOTE The Change instance is a clone and will be discarded after each exection, so modifying it would be meaningless.
NOTE Change is not available to before_new and after_new hooks.
Execute SQL statement
You can execute arbitrary SQL statement via the built-in global object hookContext
.
hookContext.executeSql("insert into worklog (str1) values ('done!');");
If you need more than just executing SQL statement, you can get an instance of java.sql.Connection
from hookContext
.
con = hookContext.getConnection();
try {
stmt = con.createStatement();
rs = stmt.executeQuery("select * from changelog");
while (rs.next()) {
print("id = " + rs.getString("id"));
}
} finally {
con.close();
con = null;
rs = null;
stmt = null;
}
NOTE These methods are not available to before_new and after_new hooks.
Invoking function
When configuring JSR-223 hook scripts, it is possible to specify a top level function to invoke. The following script contains two functions foo
and bar
and bar
takes two arguments.
// foobar.js
function foo() {
print('foo');
}
function bar(id, name) {
print(id + ':' + name);
}
To invoke these function, specify the function name using a special variable name _function
and parameter values with _arg
.
hook_before_up=js:foobar.js:_function=foo
hook_after_up=js:foobar.js:_function=bar:_arg=100:_arg=John
NOTE Some JSR-223 implementation may not support function invocation.
Invoking method
Similar to function invocation, it is also possible to invoke a method of an top level object.
// doggy.js
var dog = new Object();
dog.bark = function(who, times) {
print('bow-wow ' + times + ' times at ' + who);
});
In the hook setting, use _object
to specify the object name and _method
to specify the method name.
hook_before_up=js:doggy.js:_object=dog:_method=bark:_arg=Lucy:_arg=128
NOTE Some JSR-223 implementation may not support method invocation.
Retain variable value throughout the operation
When single up/down operation executes multiple migrations, variables are reset on each migration.
There are two ways to retain variable value throughout the operation.
-
Initialize the variable in before_up/down script and use it in before/after_each_up/down script.
// before_up hook script var counter = 1;
// before_each_up hook script print(counter++);
-
Initialize only when the variable is undefined.
if (typeof counter == 'undefined') this.counter = 1; println(counter++);
Use other languages than JavaScript
To use other JSR-223 compliant scripting language than JavaScript, you need to copy required .jar files to $MIGRATIONS_HOME/lib directory.
To write hook scripts in Groovy, for example, you will need groovy.jar and groovy-jsr223.jar.
Once the JARs are placed in $MIGRATIONS_HOME/lib directory, the rest is pretty much the same as JavaScript.
Save the following script as hello.groovy
in hooks directory with the following content...
println('Hello groovy!')
...and add the setting to the environment properties file.
hook_before_up=groovy:hello.groovy