flyway:每次迁移后运行的通用脚本

时间:2013-02-05 22:05:12

标签: migration flyway

我有一个通用的清理脚本,我想在每次迁移后运行。是否有一种很好的方法可以在每次迁移后运行此脚本(每次迁移时都不包括脚本本身的更改?)

我看到这个问题在此之前被问到Pre- and Post-migration scripts for Flyway,当时答案是否定的,不是真的。

过去1。5年的答案是否有所改变?

4 个答案:

答案 0 :(得分:3)

使用flyway 3.0,情况已经发生变化,现在可以使用回调脚本。在这种情况下,可以使用afterMigration.sql文件进行清理。

有关详细信息,请参阅http://flywaydb.org/documentation/callbacks.html

答案 1 :(得分:1)

这没有改变。立即使用任何建议的解决方法。

答案 2 :(得分:1)

我已经查看了这里的建议和Pre- and Post-migration scripts for Flyway,并想指出一个用例,我无法看到哪种解决方法(如果有的话)最适用。用例是在运行开发人员创建的迁移之前让dba创建一个还原点。

目前,通过我们的手动(非飞路)迁移过程,dba会在运行一组迁移之前创建一个还原点。如果没有还原点,迁移就可以正常运行。但是如果他们没有正确的代码(比如缺少创建列),那么回滚到oracle还原点通常更可取,以避免停机并让开发人员有时间处理修复。

我不认为要求开发人员包含一个迁移,该恢复点有意义,因为: 他们可能会忘记(它应该自动发生,没有开发人员干预) 2.根据模式的状态,可能会有不同的开始迁移,因此如果未运行包含还原点的迁移,则可能是旧的,并且数据可能在过渡期间已更改。

执行还原点的单独迁移具有类似的缺点: 1.他们必须手动创建一个新的迁移,该迁移本质上是具有不同版本号的旧迁移的副本,以执行还原点。

对于具有大量现有数据的开发模式,在开发迁移时清除模式是不切实际的,因为它早于飞路并且可能需要花费大量时间从头开始重新创建。

对于开发,理想情况下工作流程是这样的: 1.创建还原点 2.开发迁移,使用flyway运行 3.如果迁移不能按要求工作,则回滚到恢复点。

如果有一种方法可以全面自动执行步骤#1,它将允许我们使用flyway并消除对dba的需求,除非出现问题并且需要回滚。可能会有一个更多的飞行路线'解决问题的方法,但我发现的解决方案似乎不适合我们现有的工作流程。

答案 3 :(得分:0)

我们遇到了同样的问题。即,在每次迁移之前和之后总是调用一堆脚本。例如,删除和创建物化视图,授予表权限。 这些脚本不会从迁移更改为迁移,但需要执行它们。

所以我选择了org.flywaydb.core.internal.callback.SqlScriptFlywayCallback回调类并将其改编为多个文件。

我试图坚持flyway的哲学并使用以下模式。 以am__AM__开头的文件是迁移后的脚本,bi__的文件用于信息之前,依此类推。 我对脚本进行排序,以便按正确的顺序执行。

public class MultipleScriptPerCallback extends BaseFlywayCallback {
private static final Log LOG = LogFactory.getLog(SqlScriptFlywayCallback.class);
private static final String DELIMITER = "__";

private static final String BEFORE_CLEAN = "bc";
private static final String AFTER_CLEAN = "ac";
private static final String BEFORE_MIGRATE = "bm";
private static final String AFTER_MIGRATE = "am";
private static final String BEFORE_EACH_MIGRATE = "bem";
private static final String AFTER_EACH_MIGRATE = "aem";
private static final String BEFORE_VALIDATE = "bv";
private static final String AFTER_VALIDATE = "av";
private static final String BEFORE_BASELINE = "bb";
private static final String AFTER_BASELINE = "ab";
private static final String BEFORE_REPAIR = "br";
private static final String AFTER_REPAIR = "ar";
private static final String BEFORE_INFO = "bi";
private static final String AFTER_INFO = "ai";

private static final List<String> ALL_CALLBACKS = Arrays.asList(BEFORE_CLEAN, AFTER_CLEAN, BEFORE_MIGRATE, BEFORE_EACH_MIGRATE,
        AFTER_EACH_MIGRATE, AFTER_MIGRATE, BEFORE_VALIDATE, AFTER_VALIDATE, BEFORE_BASELINE, AFTER_BASELINE, BEFORE_REPAIR,
        AFTER_REPAIR, BEFORE_INFO, AFTER_INFO);

private Map<String, List<SqlScript>> scripts;

@Override
public void setFlywayConfiguration(FlywayConfiguration flywayConfiguration) {
    super.setFlywayConfiguration(flywayConfiguration);

    if (scripts == null) {
        scripts = registerScripts(flywayConfiguration);
    }
}

private Map<String, List<SqlScript>> registerScripts(FlywayConfiguration flywayConfiguration) {
    Map<String, List<SqlScript>> scripts = new HashMap<>();
    for (String callback : ALL_CALLBACKS) {
        scripts.put(callback, new ArrayList<SqlScript>());
    }

    LOG.debug(String.format("%s - Scanning for Multiple SQL callbacks ...", getClass().getSimpleName()));
    Locations locations = new Locations(flywayConfiguration.getLocations());
    Scanner scanner = new Scanner(flywayConfiguration.getClassLoader());
    String sqlMigrationSuffix = flywayConfiguration.getSqlMigrationSuffix();
    DbSupport dbSupport = dbSupport(flywayConfiguration);
    PlaceholderReplacer placeholderReplacer = createPlaceholderReplacer();
    String encoding = flywayConfiguration.getEncoding();

    for (Location location : locations.getLocations()) {
        Resource[] resources;
        try {
            resources = scanner.scanForResources(location, "", sqlMigrationSuffix);
        } catch (FlywayException e) {
            // Ignore missing locations
            continue;
        }
        for (Resource resource : resources) {
            String key = extractKeyFromFileName(resource);
            if (scripts.keySet().contains(key)) {
                LOG.debug(getClass().getSimpleName() + " - found script " + resource.getFilename() + " from location: " + location);
                List<SqlScript> sqlScripts = scripts.get(key);
                sqlScripts.add(new SqlScript(dbSupport, resource, placeholderReplacer, encoding));
            }
        }
    }

    LOG.info(getClass().getSimpleName() + " - scripts registered: " + prettyPrint(scripts));
    return scripts;
}

private String prettyPrint(Map<String, List<SqlScript>> scripts) {
    StringBuilder prettyPrint = new StringBuilder();
    boolean isFirst = true;
    for (String key : scripts.keySet()) {
        if (!isFirst) {
            prettyPrint.append("; ");
        }
        prettyPrint.append(key).append("=").append("[").append(prettyPrint(scripts.get(key))).append("]");
        isFirst = false;
    }
    return prettyPrint.toString();
}

private String prettyPrint(List<SqlScript> scripts) {
    StringBuilder prettyPrint = new StringBuilder();
    boolean isFirst = true;
    for (SqlScript script : scripts) {
        if (!isFirst) {
            prettyPrint.append(", ");
        }
        prettyPrint.append(script.getResource().getFilename());
        isFirst = false;
    }
    return prettyPrint.toString();
}

private String extractKeyFromFileName(Resource resource) {
    String filename = resource.getFilename();
    eturn filename.substring(0, (!filename.contains(DELIMITER)) ? 0 : filename.indexOf(DELIMITER)).toLowerCase();
}

private DbSupport dbSupport(FlywayConfiguration flywayConfiguration) {
    Connection connectionMetaDataTable = JdbcUtils.openConnection(flywayConfiguration.getDataSource());
    return DbSupportFactory.createDbSupport(connectionMetaDataTable, true);
}

/**
 * @return  A new, fully configured, PlaceholderReplacer.
 */
private PlaceholderReplacer createPlaceholderReplacer() {
    if (flywayConfiguration.isPlaceholderReplacement()) {
        return
            new PlaceholderReplacer(flywayConfiguration.getPlaceholders(), flywayConfiguration.getPlaceholderPrefix(),
                flywayConfiguration.getPlaceholderSuffix());
    }

    return PlaceholderReplacer.NO_PLACEHOLDERS;
}

@Override
public void beforeClean(Connection connection) {
    execute(BEFORE_CLEAN, connection);
}

@Override
public void afterClean(Connection connection) {
    execute(AFTER_CLEAN, connection);
}

@Override
public void beforeMigrate(Connection connection) {
    execute(BEFORE_MIGRATE, connection);
}

@Override
public void afterMigrate(Connection connection) {
    execute(AFTER_MIGRATE, connection);
}

@Override
public void beforeEachMigrate(Connection connection, MigrationInfo info) {
    execute(BEFORE_EACH_MIGRATE, connection);
}

@Override
public void afterEachMigrate(Connection connection, MigrationInfo info) {
    execute(AFTER_EACH_MIGRATE, connection);
}

@Override
public void beforeValidate(Connection connection) {
    execute(BEFORE_VALIDATE, connection);
}

@Override
public void afterValidate(Connection connection) {
    execute(AFTER_VALIDATE, connection);
}

@Override
public void beforeBaseline(Connection connection) {
    execute(BEFORE_BASELINE, connection);
}

@Override
public void afterBaseline(Connection connection) {
    execute(AFTER_BASELINE, connection);
}

@Override
public void beforeRepair(Connection connection) {
    execute(BEFORE_REPAIR, connection);
}

@Override
public void afterRepair(Connection connection) {
    execute(AFTER_REPAIR, connection);
}

@Override
public void beforeInfo(Connection connection) {
    execute(BEFORE_INFO, connection);
}

@Override
public void afterInfo(Connection connection) {
    execute(AFTER_INFO, connection);
}

private void execute(String key, Connection connection) {
    List<SqlScript> sqlScripts = scripts.get(key);
    LOG.debug(String.format("%s - sqlscript: %s for key: %s", getClass().getSimpleName(), sqlScripts, key));
    Collections.sort(sqlScripts, new SqlScriptLexicalComparator());
    for (SqlScript script : sqlScripts) {
        executeScript(key, connection, script);
    }
}

//Not private for testing
void executeScript(String key, Connection connection, SqlScript script) {
    LOG.info(String.format("%s - Executing SQL callback: %s : %s", getClass().getSimpleName(), key,
            script.getResource().getFilename()));
    script.execute(new JdbcTemplate(connection, 0));
}

//Not private for testing
static final class SqlScriptLexicalComparator implements Comparator<SqlScript> {
    @Override
    public int compare(SqlScript o1, SqlScript o2) {
        return Collator.getInstance().compare(o1.getResource().getFilename(), o2.getResource().getFilename());
    }
}

}