New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide pre/post-migration hooks #52

Closed
harawata opened this Issue Jun 16, 2016 · 4 comments

Comments

Projects
None yet
2 participants
@harawata
Member

harawata commented Jun 16, 2016

This is a draft and obsolete!

Please see the documentation. http://www.mybatis.org/migrations/hooks.html



Migration hook

Overview

You can write scripts that are executed before or after up/down operation.
The following hooks are supported.

  • before up/down : executed before the first migration
  • before each up/down : executed before each migration
  • after each up/down : executed after each migration
  • after up/down : executed after the last migration

SQL and JSR-223 compliant scripting languages are supported for writing hook scripts.

Quick start

Here are what you need to do to use hook scripts.

  1. Create hooks directory.
  2. Create a hook script in hooks directory.
  3. Add settings 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.

// hello.js
print('Hello, World!');

The example is written in JavaScript because JavaScript script engine is shipped with JDK.

3. Add settings to the environment properties file.

To configure Migrations, add the following line to the development.properties in the environment directory.

# development.properties
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

Keys for available hooks

As shown in the Quick Start section, the key of a hook setting specifies when to execute the script. 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

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:restart_server.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.

The below is an example for SQL hook scripts.

# 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();

If there are global variables defined in the environment properties file, they can be used in hook scripts in the same manner.

foo=bar

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());

Accessing Change object (each hook only)

In an each hook script, an instance of Change object is accessible via the global variable migrationContext.

print(migrationContext.getChange().getId());
print(migrationContext.getChange().getFilename());

NOTE: The Change instance is a clone and will be discarded after each exection, so modifying it would be meaningless.

Execute SQL statement

You can execute arbitrary SQL statement via the built-in global object migrationContext.

migrationContext.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 migrationContext.

con = migrationContext.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;
}

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.

// foobar.js
function foo() {
  print('foo');
}

function bar(id, name) {
  print(id + ':' + name);
}

To invoke a function, you need to specify the function name using a special variable name _function.

hook_before_up=js:foobar.js:_function=foo
hook_after_up=js:foobar.js:_function=bar:_arg=100:_arg=John

Notice that the two arguments passed to bar() function are specified with another special variable name _arg.

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 for 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 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.

  1. 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++);
  2. Initialize only when it 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 hello.groovy in hooks directory with the following content...

// hello.groovy
println('Hello groovy!')

...and add the setting to the environment file.

hook_before_up=groovy:hello.groovy

@harawata harawata self-assigned this Jun 16, 2016

@h3adache

This comment has been minimized.

Show comment
Hide comment
@h3adache

h3adache Jun 20, 2016

Member

This is a good writeup @harawata . Feels like unit testing before[Class]/after[Class] but also allows better migration control (hooks lol) for people. The before each/after might be better suited if it were tied to specific migrations? // @BEFORE // @AFTER (similar to the current // @UNDO) perhaps? Just a thought though.

Member

h3adache commented Jun 20, 2016

This is a good writeup @harawata . Feels like unit testing before[Class]/after[Class] but also allows better migration control (hooks lol) for people. The before each/after might be better suited if it were tied to specific migrations? // @BEFORE // @AFTER (similar to the current // @UNDO) perhaps? Just a thought though.

@harawata

This comment has been minimized.

Show comment
Hide comment
@harawata

harawata Jun 20, 2016

Member

Thank you very much for the review, @h3adache !

The before each/after might be better suited if it were tied to specific migrations?

In an each hook, it is possible to check the ID or the description of the migration via the global variable like migrationContext.getChange() (only in JSR-223 scripts).
I think it's flexible enough...at least for the first implementation.

The work is mostly done and I'll be able to commit the changes soon.
Hope it meets some company/project specific requirements!

Member

harawata commented Jun 20, 2016

Thank you very much for the review, @h3adache !

The before each/after might be better suited if it were tied to specific migrations?

In an each hook, it is possible to check the ID or the description of the migration via the global variable like migrationContext.getChange() (only in JSR-223 scripts).
I think it's flexible enough...at least for the first implementation.

The work is mostly done and I'll be able to commit the changes soon.
Hope it meets some company/project specific requirements!

@harawata harawata closed this in 1f13528 Jun 20, 2016

@harawata harawata added this to the 3.3.0 milestone Jun 20, 2016

@harawata

This comment has been minimized.

Show comment
Hide comment
@harawata

harawata Jun 21, 2016

Member

I will make some changes later (will update the spec too).

Member

harawata commented Jun 21, 2016

I will make some changes later (will update the spec too).

@harawata harawata reopened this Jun 21, 2016

harawata added a commit that referenced this issue Jun 21, 2016

@harawata

This comment has been minimized.

Show comment
Hide comment
@harawata

harawata Jun 21, 2016

Member

Done.

Member

harawata commented Jun 21, 2016

Done.

@harawata harawata closed this Jun 21, 2016

harawata added a commit that referenced this issue Jun 23, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment