【问题标题】:flyway: common script to run after every migrationflyway:每次迁移后运行的通用脚本
【发布时间】:2013-01-20 23:46:10
【问题描述】:

我有一个通用的清理脚本,我想在每次迁移后运行它。有没有一种好方法可以在每次迁移后运行此脚本(每次迁移时都将脚本本身作为更改包含在内?)

我看到之前在这里Pre- and Post-migration scripts for Flyway 已经问过这个问题,当时的答案是不,不是。

在过去的 1.5 年里,答案有没有改变?

【问题讨论】:

  • 不是真正的答案,而是一种解决方法:我解决了一个类似的问题,方法是在开始 flyway 迁移之前从 SCHEMA_VERSION 表中删除此类脚本的条目。当没有条目时,flyway 会再次应用脚本。
  • 嘿。如果没有其他答案出现,我会记住这一点。谢谢。

标签: migration flyway


【解决方案1】:

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

更多信息请参见http://flywaydb.org/documentation/callbacks.html。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      现在,通过我们的手动(非 Flyway)迁移过程,dba 在运行一组迁移之前创建一个还原点。如果没有还原点,迁移将运行良好。但如果他们没有正确的代码(比如缺少创建列),通常最好回滚到 oracle 还原点,以避免停机并让开发人员有时间进行修复。

      我认为要求开发人员包含执行该还原点的迁移没有意义,因为: 1. 他们可能会忘记(它应该自动发生,无需开发人员干预) 2. 根据架构的状态,可能会有不同的开始迁移,所以如果包含还原点的那个没有运行,它可能是旧的,并且数据可能在此期间发生了变化。

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

      对于具有大量现有数据的开发架构,在开发迁移时清理架构是不切实际的,因为它早于 flyway,并且可能需要大量时间从头开始重新创建。

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

      如果有一种方法可以全面自动化第 1 步,它将允许我们使用 flyway 并消除对 dba 的需求,除非出现问题并且需要回滚。可能有一种更“可行”的方法来解决问题,但我发现的解决方法似乎不适合我们现有的工作流程。

      【讨论】:

        【解决方案4】:

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

        所以我采用了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());
            }
        }
        

        }

        【讨论】:

          猜你喜欢
          • 2020-08-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-05
          • 2018-02-28
          • 2023-03-26
          • 2021-11-22
          • 2017-05-31
          相关资源
          最近更新 更多