flyway实战

flyway是一款用来管理数据库版本的工具框架

一, 添加依赖

<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
</dependency>

二, 编写配置类

MySQLDatabase

/*
 * Copyright (C) Red Gate Software Ltd 2010-2021
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flywaydb.core.internal.database.mysql;

import lombok.CustomLog;
import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.api.MigrationType;
import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.internal.database.base.Database;
import org.flywaydb.core.internal.database.base.BaseDatabaseType;
import org.flywaydb.core.internal.database.base.Table;
import org.flywaydb.core.internal.database.mysql.mariadb.MariaDBDatabaseType;
import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;
import org.flywaydb.core.internal.jdbc.StatementInterceptor;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class MySQLDatabase extends Database<MySQLConnection> {
  // See https://mariadb.com/kb/en/version/
  private static final Pattern MARIADB_VERSION_PATTERN = Pattern.compile("(\\d+\\.\\d+)\\.\\d+(-\\d+)*-MariaDB(-\\w+)*");
  private static final Pattern MARIADB_WITH_MAXSCALE_VERSION_PATTERN = Pattern.compile("(\\d+\\.\\d+)\\.\\d+(-\\d+)* (\\d+\\.\\d+)\\.\\d+(-\\d+)*-maxscale(-\\w+)*");
  private static final Pattern MYSQL_VERSION_PATTERN = Pattern.compile("(\\d+\\.\\d+)\\.\\d+\\w*");
  /**
   * Whether this is a Percona XtraDB Cluster in strict mode.
   */
  private final boolean pxcStrict;
  /**
   * Whether this database is enforcing GTID consistency.
   */
  private final boolean gtidConsistencyEnforced;
  /**
   * Whether the event scheduler table is queryable.
   */
  final boolean eventSchedulerQueryable;

  public MySQLDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
    super(configuration, jdbcConnectionFactory, statementInterceptor);

    JdbcTemplate jdbcTemplate = new JdbcTemplate(rawMainJdbcConnection, databaseType);
    pxcStrict = isMySQL() && isRunningInPerconaXtraDBClusterWithStrictMode(jdbcTemplate);
    gtidConsistencyEnforced = isMySQL() && isRunningInGTIDConsistencyMode(jdbcTemplate);
    eventSchedulerQueryable = false;
  }

  private static boolean isEventSchedulerQueryable(JdbcTemplate jdbcTemplate) {
    try {
      // Attempt query
      jdbcTemplate.queryForString("SELECT event_name FROM information_schema.events LIMIT 1");
      return true;
    } catch (SQLException e) {
      log.debug("Detected unqueryable MariaDB event scheduler, most likely due to it being OFF or DISABLED.");
      return false;
    }
  }

  static boolean isRunningInPerconaXtraDBClusterWithStrictMode(JdbcTemplate jdbcTemplate) {
    try {
      String pcx_strict_mode = jdbcTemplate.queryForString(
          "select VARIABLE_VALUE from performance_schema.global_variables"
              + " where variable_name = 'pxc_strict_mode'");
      if ("ENFORCING".equals(pcx_strict_mode) || "MASTER".equals(pcx_strict_mode)) {
        log.debug("Detected Percona XtraDB Cluster in strict mode");
        return true;
      }
    } catch (SQLException e) {
      log.debug("Unable to detect whether we are running in a Percona XtraDB Cluster. Assuming not to be.");
    }

    return false;
  }

  static boolean isRunningInGTIDConsistencyMode(JdbcTemplate jdbcTemplate) {
    try {
      String gtidConsistency = jdbcTemplate.queryForString("SELECT @@GLOBAL.ENFORCE_GTID_CONSISTENCY");
      if ("ON".equals(gtidConsistency)) {
        log.debug("Detected GTID consistency being enforced");
        return true;
      }
    } catch (SQLException e) {
      log.debug("Unable to detect whether database enforces GTID consistency. Assuming not.");
    }

    return false;
  }

  boolean isMySQL() {
    return databaseType instanceof MySQLDatabaseType;
  }

  boolean isMariaDB() {
    return databaseType instanceof MariaDBDatabaseType;
  }

  boolean isPxcStrict() {
    return pxcStrict;
  }

  /*
   * CREATE TABLE ... AS SELECT ... cannot be used in three scenarios:
   * - Percona XtraDB Cluster in strict mode doesn't support it
   * - TiDB doesn't support it (overridden elsewhere)
   * - When GTID consistency is being enforced. Note that if GTID_MODE is ON, then ENFORCE_GTID_CONSISTENCY is
   * necessarily ON as well.
   */
  protected boolean isCreateTableAsSelectAllowed() {
    return !pxcStrict && !gtidConsistencyEnforced;
  }

  @Override
  public String getRawCreateScript(Table table, boolean baseline) {
    String tablespace =



        configuration.getTablespace() == null
            ? ""
            : " TABLESPACE \"" + configuration.getTablespace() + "\"";

    String baselineMarker = "";
    if (baseline) {
      if (isCreateTableAsSelectAllowed()) {
        baselineMarker = " AS SELECT" +
            "     1 as \"installed_rank\"," +
            "     '" + configuration.getBaselineVersion() + "' as \"version\"," +
            "     '" + configuration.getBaselineDescription() + "' as \"description\"," +
            "     '" + MigrationType.BASELINE + "' as \"type\"," +
            "     '" + configuration.getBaselineDescription() + "' as \"script\"," +
            "     NULL as \"checksum\"," +
            "     '" + getInstalledBy() + "' as \"installed_by\"," +
            "     CURRENT_TIMESTAMP as \"installed_on\"," +
            "     0 as \"execution_time\"," +
            "     TRUE as \"success\"\n";
      } else {
        // Revert to regular insert, which unfortunately is not safe in concurrent scenarios
        // due to MySQL implicit commits after DDL statements.
        baselineMarker = ";\n" + getBaselineStatement(table);
      }
    }

    return "CREATE TABLE " + table + " (\n" +
        "    `installed_rank` INT NOT NULL,\n" +
        "    `version` VARCHAR(50),\n" +
        "    `description` VARCHAR(200) NOT NULL,\n" +
        "    `type` VARCHAR(20) NOT NULL,\n" +
        "    `script` VARCHAR(1000) NOT NULL,\n" +
        "    `checksum` INT,\n" +
        "    `installed_by` VARCHAR(100) NOT NULL,\n" +
        "    `installed_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n" +
        "    `execution_time` INT NOT NULL,\n" +
        "    `success` BOOL NOT NULL,\n" +
        "    CONSTRAINT " + getConstraintName(table.getName()) + " PRIMARY KEY (`installed_rank`)\n" +
        ")" + tablespace + " ENGINE=InnoDB" +
        baselineMarker +
        ";\n" +
        "CREATE INDEX `" + table.getName() + "_s_idx` ON " + table + " (`success`);";
  }

  protected String getConstraintName(String tableName) {
    return "`" + tableName + "_pk`";
  }

  @Override
  protected MySQLConnection doGetConnection(Connection connection) {
    return new MySQLConnection(this, connection);
  }

  @Override
  protected MigrationVersion determineVersion() {
    // Ignore the version from the JDBC metadata and use the version returned by the database since proxies such as
    // Azure or ProxySQL return incorrect versions
    String selectVersionOutput = BaseDatabaseType.getSelectVersionOutput(rawMainJdbcConnection);
    if (databaseType instanceof MariaDBDatabaseType) {
      return extractMariaDBVersionFromString(selectVersionOutput);
    }
    return extractMySQLVersionFromString(selectVersionOutput);
  }

  static MigrationVersion extractMySQLVersionFromString(String selectVersionOutput) {
    return extractVersionFromString(selectVersionOutput, MYSQL_VERSION_PATTERN);
  }

  static MigrationVersion extractMariaDBVersionFromString(String selectVersionOutput) {
    return extractVersionFromString(selectVersionOutput, MARIADB_VERSION_PATTERN, MARIADB_WITH_MAXSCALE_VERSION_PATTERN);
  }

  /*
   * Given a version string that may contain unwanted text, extract out the version part.
   */
  private static MigrationVersion extractVersionFromString(String versionString, Pattern... patterns) {
    for (Pattern pattern : patterns) {
      Matcher matcher = pattern.matcher(versionString);
      if (matcher.find()) {
        return MigrationVersion.fromVersion(matcher.group(1));
      }
    }
    throw new FlywayException("Unable to determine version from '" + versionString + "'");
  }
























  @Override
  public final void ensureSupported() {
    ensureDatabaseIsRecentEnough("5.1");
    if (databaseType instanceof MariaDBDatabaseType) {

      ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.3", org.flywaydb.core.internal.license.Edition.ENTERPRISE);

      recommendFlywayUpgradeIfNecessary("10.6");
    } else {

      ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("8.0", org.flywaydb.core.internal.license.Edition.ENTERPRISE);

      recommendFlywayUpgradeIfNecessary("8.0");
    }
  }

  @Override
  protected String doGetCurrentUser() throws SQLException {
    return getMainConnection().getJdbcTemplate().queryForString("SELECT SUBSTRING_INDEX(USER(),'@',1)");
  }

  @Override
  public boolean supportsDdlTransactions() {
    return false;
  }

  @Override
  public boolean supportsChangingCurrentSchema() {
    return true;
  }

  @Override
  public String getBooleanTrue() {
    return "1";
  }

  @Override
  public String getBooleanFalse() {
    return "0";
  }

  @Override
  public String doQuote(String identifier) {
    return "`" + identifier + "`";
  }

  @Override
  public boolean catalogIsSchema() {
    return true;
  }

  @Override
  public boolean useSingleConnection() {
    return !pxcStrict;
  }
}

MySQLNamedLockTemplate

/*
 * Copyright (C) Red Gate Software Ltd 2010-2021
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flywaydb.core.internal.database.mysql;

import java.sql.SQLException;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.api.FlywayException;
import org.flywaydb.core.internal.exception.FlywaySqlException;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;

/**
 * Spring-like template for executing with MySQL named locks.
 */
@Slf4j
public class MySQLNamedLockTemplate {

  /**
   * The connection for the named lock.
   */
  private final JdbcTemplate jdbcTemplate;

  private final String lockName;

  /**
   * Creates a new named lock template for this connection.
   *
   * @param jdbcTemplate  The jdbcTemplate for the connection.
   * @param discriminator A number to discriminate between locks.
   */
  MySQLNamedLockTemplate(JdbcTemplate jdbcTemplate, int discriminator) {
    this.jdbcTemplate = jdbcTemplate;
    lockName = "Flyway-" + discriminator;
  }

  /**
   * Executes this callback with a named lock.
   *
   * @param callable The callback to execute.
   * @return The result of the callable code.
   */
  public <T> T execute(Callable<T> callable) {
    try {
      lock();
      return callable.call();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to acquire MySQL named lock: " + lockName, e);
    } catch (Exception e) {
      RuntimeException rethrow;
      if (e instanceof RuntimeException) {
        rethrow = (RuntimeException) e;
      } else {
        rethrow = new FlywayException(e);
      }
      throw rethrow;
    } finally {
      try {
        //FIXME ocean base 没有GET_LOCK 需要通过其他方式来实现flyway锁
        if (1 == 1) {
        } else {
          jdbcTemplate.execute("SELECT RELEASE_LOCK('" + lockName + "')");
        }
      } catch (SQLException e) {
        log.error("Unable to release MySQL named lock: " + lockName, e);
      }
    }
  }

  private void lock() throws SQLException {
    while (!tryLock()) {
      try {
        Thread.sleep(100L);
      } catch (InterruptedException e) {
        throw new FlywayException(
            "Interrupted while attempting to acquire MySQL named lock: " + lockName, e);
      }
    }
  }

  private boolean tryLock() throws SQLException {
    //FIXME ocean base 没有GET_LOCK 需要通过其他方式来实现flyway锁
    if (1 == 1) {
      return true;
    }
    return jdbcTemplate.queryForInt("SELECT GET_LOCK(?,10)", lockName) == 1;
  }
}

Databse类

/*
 * Copyright (C) Red Gate Software Ltd 2010-2021
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flywaydb.core.internal.database.base;

import lombok.CustomLog;
import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.api.MigrationType;
import org.flywaydb.core.api.MigrationVersion;
import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.internal.database.DatabaseType;
import org.flywaydb.core.internal.exception.FlywayDbUpgradeRequiredException;
import org.flywaydb.core.internal.exception.FlywaySqlException;
import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;
import org.flywaydb.core.internal.jdbc.StatementInterceptor;
import org.flywaydb.core.internal.license.Edition;
import org.flywaydb.core.internal.license.FlywayEditionUpgradeRequiredException;
import org.flywaydb.core.internal.resource.StringResource;
import org.flywaydb.core.internal.sqlscript.Delimiter;
import org.flywaydb.core.internal.sqlscript.SqlScript;
import org.flywaydb.core.internal.sqlscript.SqlScriptFactory;
import org.flywaydb.core.internal.util.AbbreviationUtils;

import java.io.Closeable;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;

/**
 * Abstraction for database-specific functionality.
 */
@Slf4j
public abstract class Database<C extends Connection> implements Closeable {
  protected final DatabaseType databaseType;
  protected final Configuration configuration;
  protected final StatementInterceptor statementInterceptor;
  protected final JdbcConnectionFactory jdbcConnectionFactory;
  protected final DatabaseMetaData jdbcMetaData;
  protected JdbcTemplate jdbcTemplate;
  private C migrationConnection;
  private C mainConnection;
  /**
   * The main JDBC connection, without any wrapping.
   */
  protected final java.sql.Connection rawMainJdbcConnection;
  /**
   * The 'major.minor' version of this database.
   */
  private MigrationVersion version;
  /**
   * The user who applied the migrations.
   */
  private String installedBy;

  public Database(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
    this.databaseType = jdbcConnectionFactory.getDatabaseType();
    this.configuration = configuration;
    this.rawMainJdbcConnection = jdbcConnectionFactory.openConnection();
    try {
      this.jdbcMetaData = rawMainJdbcConnection.getMetaData();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to get metadata for connection", e);
    }
    this.jdbcTemplate = new JdbcTemplate(rawMainJdbcConnection, databaseType);
    this.jdbcConnectionFactory = jdbcConnectionFactory;
    this.statementInterceptor = statementInterceptor;
  }

  /**
   * Retrieves a Flyway Connection for this JDBC connection.
   */
  private C getConnection(java.sql.Connection connection) {
    return doGetConnection(connection);
  }

  /**
   * Retrieves a Flyway Connection for this JDBC connection.
   */
  protected abstract C doGetConnection(java.sql.Connection connection);

  /**
   * Ensure Flyway supports this version of this database.
   */
  public abstract void ensureSupported();

  /**
   * @return The 'major.minor' version of this database.
   */
  public final MigrationVersion getVersion() {
    if (version == null) {
      version = determineVersion();
    }
    return version;
  }

  protected final void ensureDatabaseIsRecentEnough(String oldestSupportedVersion) {
  /*  if (!getVersion().isAtLeast(oldestSupportedVersion)) {
      throw new FlywayDbUpgradeRequiredException(
          databaseType,
          computeVersionDisplayName(getVersion()),
          computeVersionDisplayName(MigrationVersion.fromVersion(oldestSupportedVersion)));
    }*/
  }

  /**
   * Ensure this database it at least as recent as this version otherwise suggest upgrade to this higher edition of
   * Flyway.
   */
  protected final void ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition(String oldestSupportedVersionInThisEdition,
      Edition editionWhereStillSupported) {
  /*  if (!getVersion().isAtLeast(oldestSupportedVersionInThisEdition)) {
      throw new FlywayEditionUpgradeRequiredException(
          editionWhereStillSupported,
          databaseType,
          computeVersionDisplayName(getVersion()));
    }*/
  }

  protected final void recommendFlywayUpgradeIfNecessary(String newestSupportedVersion) {
    if (getVersion().isNewerThan(newestSupportedVersion)) {
      recommendFlywayUpgrade(newestSupportedVersion);
    }
  }

  protected final void recommendFlywayUpgradeIfNecessaryForMajorVersion(String newestSupportedVersion) {
    if (getVersion().isMajorNewerThan(newestSupportedVersion)) {
      recommendFlywayUpgrade(newestSupportedVersion);
    }
  }

  protected final void notifyDatabaseIsNotFormallySupported() {
    String message = "Support for " + databaseType + " is provided only on a community-led basis, and is not formally supported by Redgate";

    log.warn(message);
  }

  private void recommendFlywayUpgrade(String newestSupportedVersion) {
    String message = "Flyway upgrade recommended: " + databaseType + " " + computeVersionDisplayName(getVersion())
        + " is newer than this version of Flyway and support has not been tested."
        + " The latest supported version of " + databaseType + " is " + newestSupportedVersion + ".";
    log.warn(message);
  }

  /**
   * Compute the user-friendly display name for this database version.
   */
  protected String computeVersionDisplayName(MigrationVersion version) {
    return version.getVersion();
  }

  public Delimiter getDefaultDelimiter() {
    return Delimiter.SEMICOLON;
  }

  /**
   * @return The name of the database, by default as determined by JDBC.
   */
  public final String getCatalog() {
    try {
      return doGetCatalog();
    } catch (SQLException e) {
      throw new FlywaySqlException("Error retrieving the database name", e);
    }
  }

  protected String doGetCatalog() throws SQLException {
    return getMainConnection().getJdbcConnection().getCatalog();
  }

  public final String getCurrentUser() {
    try {
      return doGetCurrentUser();
    } catch (SQLException e) {
      throw new FlywaySqlException("Error retrieving the database user", e);
    }
  }

  protected String doGetCurrentUser() throws SQLException {
    return jdbcMetaData.getUserName();
  }

  public abstract boolean supportsDdlTransactions();

  public abstract boolean supportsChangingCurrentSchema();







  /**
   * @return The representation of the value {@code true} in a boolean column.
   */
  public abstract String getBooleanTrue();

  /**
   * @return The representation of the value {@code false} in a boolean column.
   */
  public abstract String getBooleanFalse();

  /**
   * Quotes these identifiers for use in SQL queries. Multiple identifiers will be quoted and separated by a dot.
   */
  public final String quote(String... identifiers) {
    StringBuilder result = new StringBuilder();

    boolean first = true;
    for (String identifier : identifiers) {
      if (!first) {
        result.append(".");
      }
      first = false;
      result.append(doQuote(identifier));
    }

    return result.toString();
  }

  /**
   * Quotes this identifier for use in SQL queries.
   */
  protected abstract String doQuote(String identifier);

  /**
   * @return {@code true} if this database uses a catalog to represent a schema, or {@code false} if a schema is
   * simply a schema.
   */
  public abstract boolean catalogIsSchema();

  /**
   * @return Whether to use a single connection for both schema history table management and applying migrations.
   */
  public boolean useSingleConnection() {
    return false;
  }

  public DatabaseMetaData getJdbcMetaData() {
    return jdbcMetaData;
  }

  /**
   * @return The main connection used to manipulate the schema history.
   */
  public final C getMainConnection() {
    if (mainConnection == null) {
      this.mainConnection = getConnection(rawMainJdbcConnection);
    }
    return mainConnection;
  }

  /**
   * @return The migration connection used to apply migrations.
   */
  public final C getMigrationConnection() {
    if (migrationConnection == null) {
      if (useSingleConnection()) {
        this.migrationConnection = getMainConnection();
      } else {
        this.migrationConnection = getConnection(jdbcConnectionFactory.openConnection());
      }
    }
    return migrationConnection;
  }

  /**
   * @return The major and minor version of the database.
   */
  protected MigrationVersion determineVersion() {
    try {
      return MigrationVersion.fromVersion(jdbcMetaData.getDatabaseMajorVersion() + "." + jdbcMetaData.getDatabaseMinorVersion());
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to determine the major version of the database", e);
    }
  }

  /**
   * Retrieves the script used to create the schema history table.
   *
   * @param sqlScriptFactory The factory used to create the SQL script.
   * @param table            The table to create.
   * @param baseline         Whether to include the creation of a baseline marker.
   */
  public final SqlScript getCreateScript(SqlScriptFactory sqlScriptFactory, Table table, boolean baseline) {
    return sqlScriptFactory.createSqlScript(new StringResource(getRawCreateScript(table, baseline)), false, null);
  }

  public abstract String getRawCreateScript(Table table, boolean baseline);

  public String getInsertStatement(Table table) {
    return "INSERT INTO " + table
        + " (" + quote("installed_rank")
        + ", " + quote("version")
        + ", " + quote("description")
        + ", " + quote("type")
        + ", " + quote("script")
        + ", " + quote("checksum")
        + ", " + quote("installed_by")
        + ", " + quote("execution_time")
        + ", " + quote("success")
        + ")"
        + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
  }

  public final String getBaselineStatement(Table table) {
    return String.format(getInsertStatement(table).replace("?", "%s"),
        1,
        "'" + configuration.getBaselineVersion() + "'",
        "'" + AbbreviationUtils.abbreviateDescription(configuration.getBaselineDescription()) + "'",
        "'" + MigrationType.BASELINE + "'",
        "'" + AbbreviationUtils.abbreviateScript(configuration.getBaselineDescription()) + "'",
        "NULL",
        "'" + installedBy + "'",
        0,
        getBooleanTrue()
    );
  }

  public String getSelectStatement(Table table) {
    return "SELECT " + quote("installed_rank")
        + "," + quote("version")
        + "," + quote("description")
        + "," + quote("type")
        + "," + quote("script")
        + "," + quote("checksum")
        + "," + quote("installed_on")
        + "," + quote("installed_by")
        + "," + quote("execution_time")
        + "," + quote("success")
        + " FROM " + table
        + " WHERE " + quote("installed_rank") + " > ?"
        + " ORDER BY " + quote("installed_rank");
  }

  public final String getInstalledBy() {
    if (installedBy == null) {
      installedBy = configuration.getInstalledBy() == null ? getCurrentUser() : configuration.getInstalledBy();
    }
    return installedBy;
  }

  public void close() {
    if (!useSingleConnection() && migrationConnection != null) {
      migrationConnection.close();
    }
    if (mainConnection != null) {
      mainConnection.close();
    }
  }

  public DatabaseType getDatabaseType() {
    return databaseType;
  }

  public boolean supportsEmptyMigrationDescription() { return true; }

  public boolean supportsMultiStatementTransactions() { return true; }

  /**
   * Cleans all the objects in this database that need to be cleaned before each schema.
   */
  public void cleanPreSchemas() {
    try {
      doCleanPreSchemas();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to clean database " + this, e);
    }
  }

  /**
   * Cleans all the objects in this database that need to be cleaned before each schema.
   *
   * @throws SQLException when the clean failed.
   */
  protected void doCleanPreSchemas() throws SQLException { }

  /**
   * Cleans all the objects in this database that need to be cleaned after each schema.
   *
   * @param schemas The list of schemas managed by Flyway.
   */
  public void cleanPostSchemas(Schema[] schemas) {
    try {
      doCleanPostSchemas(schemas);
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to clean schema " + this, e);
    }
  }

  /**
   * Cleans all the objects in this database that need to be cleaned after each schema.
   *
   * @param schemas The list of schemas managed by Flyway.
   * @throws SQLException when the clean failed.
   */
  protected void doCleanPostSchemas(Schema[] schemas) throws SQLException { }

  public Schema[] getAllSchemas() {
    throw new UnsupportedOperationException("Getting all schemas not supported for " + getDatabaseType().getName());
  }
}

Schema类

/*
 * Copyright (C) Red Gate Software Ltd 2010-2021
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.flywaydb.core.internal.database.base;

import lombok.CustomLog;
import lombok.extern.slf4j.Slf4j;
import org.flywaydb.core.internal.exception.FlywaySqlException;
import org.flywaydb.core.internal.jdbc.JdbcTemplate;
import org.flywaydb.core.internal.jdbc.JdbcUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public abstract class Schema<D extends Database, T extends Table> {
  protected final JdbcTemplate jdbcTemplate;
  protected final D database;
  protected final String name;

  /**
   * @param jdbcTemplate The Jdbc Template for communicating with the DB.
   * @param database     The database-specific support.
   * @param name         The name of the schema.
   */
  public Schema(JdbcTemplate jdbcTemplate, D database, String name) {
    this.jdbcTemplate = jdbcTemplate;
    this.database = database;
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public boolean exists() {
    try {
      return doExists();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to check whether schema " + this + " exists", e);
    }
  }

  /**
   * Checks whether this schema exists.
   *
   * @throws SQLException when the check failed.
   */
  protected abstract boolean doExists() throws SQLException;

  public boolean empty() {
    try {
      return  doEmpty();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to check whether schema " + this + " is empty", e);
    }
  }

  /**
   * Checks whether this schema is empty.
   *
   * @throws SQLException when the check failed.
   */
  protected abstract boolean doEmpty() throws SQLException;

  /**
   * Creates this schema in the database.
   */
  public void create() {
    try {
      log.info("Creating schema " + this + " ...");
      doCreate();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to create schema " + this, e);
    }
  }

  /**
   * Creates this schema in the database.
   *
   * @throws SQLException when the creation failed.
   */
  protected abstract void doCreate() throws SQLException;

  /**
   * Drops this schema from the database.
   */
  public void drop() {
    try {
      doDrop();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to drop schema " + this, e);
    }
  }

  /**
   * Drops this schema from the database.
   *
   * @throws SQLException when the drop failed.
   */
  protected abstract void doDrop() throws SQLException;

  /**
   * Cleans all the objects in this schema.
   */
  public void clean() {
    try {
      doClean();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to clean schema " + this, e);
    }
  }

  /**
   * Cleans all the objects in this schema.
   *
   * @throws SQLException when the clean failed.
   */
  protected abstract void doClean() throws SQLException;

  /**
   * Retrieves all the tables in this schema.
   */
  public T[] allTables() {
    try {
      return doAllTables();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to retrieve all tables in schema " + this, e);
    }
  }

  /**
   * Retrieves all the tables in this schema.
   *
   * @throws SQLException when the retrieval failed.
   */
  protected abstract T[] doAllTables() throws SQLException;

  /**
   * Retrieves all the types in this schema.
   */
  protected final Type[] allTypes() {
    ResultSet resultSet = null;
    try {
      resultSet = database.jdbcMetaData.getUDTs(null, name, null, null);

      List<Type> types = new ArrayList<>();
      while (resultSet.next()) {
        types.add(getType(resultSet.getString("TYPE_NAME")));
      }

      return types.toArray(new Type[0]);
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to retrieve all types in schema " + this, e);
    } finally {
      JdbcUtils.closeResultSet(resultSet);
    }
  }

  /**
   * Retrieves the type with this name in this schema.
   */
  protected Type getType(String typeName) {
    return null;
  }

  /**
   * Retrieves the table with this name in this schema.
   */
  public abstract Table getTable(String tableName);

  /**
   * Retrieves the function with this name in this schema.
   */
  public Function getFunction(String functionName, String... args) {
    throw new UnsupportedOperationException("getFunction()");
  }

  /**
   * Retrieves all the functions in this schema.
   */
  protected final Function[] allFunctions() {
    try {
      return doAllFunctions();
    } catch (SQLException e) {
      throw new FlywaySqlException("Unable to retrieve all functions in schema " + this, e);
    }
  }

  /**
   * Retrieves all the functions in this schema.
   *
   * @throws SQLException when the retrieval failed.
   */
  protected Function[] doAllFunctions() throws SQLException {
    return new Function[0];
  }

  /**
   * @return The quoted name of this schema.
   */
  @Override
  public String toString() {
    return database.quote(name);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Schema schema = (Schema) o;
    return name.equals(schema.name);
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

三, flyway配置参数

spring.flyway.enabled=true
spring.flyway.url=jdbc:mysql://172.16.1.187:2883/ob_test?userSSL=false&useUnicode=true&characterEncoding=UTF-8&sslMode=DISABLED&serverTimeZone=GMT%2B8
spring.flyway.user=${spring.r2dbc.username}
spring.flyway.driver-class-name=com.mysql.jdbc.Driver
spring.flyway.password=${spring.r2dbc.password}

# ??sql???????????db/migration
spring.flyway.locations=classpath:db/migration
# ??sql????????????V
spring.flyway.sql-migration-prefix=V
# ??sql?????????????2????__
spring.flyway.sql-migration-separator=__
# ??sql?????????
spring.flyway.sql-migration-suffixes=.sql
# ????????????true
spring.flyway.validate-on-migrate=true
# ?????????????????????????????????schema_version?
spring.flyway.baseline-on-migrate=true

四, 在resources目录下创建db.migration目录并在该目录下创建sql文件, V1.0.1__init.sql

CREATE TABLE `QLFieldTest` (
                                `id` bigint(20) NOT NULL AUTO_INCREMENT,
                                `longText` text DEFAULT NULL,
                                `updateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
                                `createBy` varchar(100) NOT NULL,
                                `createTime` datetime DEFAULT NULL
) AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 ROW_FORMAT = COMPACT COMPRESSION = 'zstd_1.3.8' REPLICA_NUM = 1 BLOCK_SIZE = 16384 USE_BLOOM_FILTER = FALSE TABLET_SIZE = 134217728 PCTFREE = 0;

类似的其他sql文件同理

启动项目后,会生成一张表: flyway_schema_history

参考:

Flyway快速上手教程 - 简书

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Loren_云淡风轻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值