Article From:https://www.cnblogs.com/jpfss/p/9967700.html

Official documents

https://flywaydb.org/getstarted/firststeps/api[https://flywaydb.org/getstarted/firststeps/api]

Introductory example

JavaCode

package foobar;

import org.flywaydb.core.Flyway;

public class App {
    public static void main(String[] args) {
        Flyway flyway = new Flyway();
        // specify data source
        flyway.setDataSource("jdbc:mysql://localhost/test", "root", "root");
        // Start data migration
        flyway.migrate();
    }
}

Add the SQL file db/migration/V1_u Create_person_table.sql under classpath

create table PERSON (
    ID int not null,
    NAME varchar(100) not null
);

Run the program and create PERSON tables automatically in the test database.

Subsequently, new tables and fields need only be added to the db/migration directory in the form of V${version} ${name}. sql. The version value increases in turn, such as V2 name. sql, V3 name.SQL.

Principle introduction

FlywayThe implementation principle of database migration is to find a rule-compliant database migration script from a classpath or file system, such as a file named V${version}${name}.sql under the db/migration directory, and to follow the script according to v${version}${name}.Ersion is sorted and executed sequentially. The executed script is stored as a record in the schema_version table. When the next migration is performed, the script is judged to have been executed and skipped.

MigrationResolverThe interface is responsible for finding database migration scripts by resolveMigrations (), which are represented by ResolvedMigration objects. Migration Resolver contains a variety of implementation classes, such as SqlMigRatinResolver finds SQL files from the classpath. Queries are implemented by Scanner class, Location class specifies the query path, and SQL file naming rules need to conform to V${version}${name}.The prefix V, the suffix. SQL and the separator in the rules are defined in the Flyway Configuration interface. Another implementation class, JdbcMigration Resolver, finds the implementation of JdbcMi from the classpathClasses of integration interface, the naming rules of classes need to conform to V${version}${name}. You need to extend your implementation class to inherit BaseMigration Resolver.

MetaDataTableThe interface is responsible for finding the executed database migration scripts by findAppliedMigrations (), and the executed database migration scripts are represented by the AppliedMigration object. MetaDataTable has only one implementation class, MeTaDataTableImpl, which queries all records from the database schema_version table.

ResolvedMigrationThe set contains the applied Migration set that has been executed. Before executing the Resolved Migration, you need to compare the Applied Migration to find the executed and non-executed Resolved Migration.The comparison is implemented by Migration InfoServiceImpl. refresh (). Resolved Migration that has been executed needs to verify that the file has changed, and if it has changed, it prompts an error. Unexecuted ResolvedMigThe rations are executed in turn, and the results are recorded in the schema_version table.

FlywayMain methods:

public class Flyway {
   /**Database migration * /
   public int migrate();
   /**Check changes in migration operations performed*/
   public void validate();
   /**Database cleaning * /
   public void clean();
   /**Setting the base version of the database*/
   public void baseline();
   /**Delete migration records for execution errors*/
   public void repair();
   /**Prepare to execute the environment and perform the Command operation. The above methods call execute () to execute the operation */
   <T> T execute(Command<T> command);
}

Next, we analyze the main logic of Flyway. migrate () code execution.

public void migrate() {
  //Prepare the parameters required for Command. execute () execution by Flyway. execute ()
  return execute(new Command<Integer>() {
    public Integer execute(Connection connectionMetaDataTable,
      MigrationResolver migrationResolver,  MetaDataTable metaDataTable, 
      DbSupport dbSupport, Schema[] schemas, FlywayCallback[] flywayCallbacks) {
      //To simplify the code, ignore parameter passing
      doMigrate();

    }
  });
}

private void doMigrate() {
  //Verify changes to migration operations that have been performed
  if (validateOnMigrate) {
    doValidate(connectionMetaDataTable, dbSupport, migrationResolver,
      metaDataTable, schemas, flywayCallbacks, true);
  }
  
  //If data migration has not been done, that is, there is no data in the schema_version table.
  //And if the database is not empty, insert a baseline message
  if (!metaDataTable.exists()) {
    //The database is not empty.
    if (!nonEmptySchemas.isEmpty()) {
      //Insert a baseline message
      new DbBaseline(connectionMetaDataTable, dbSupport, metaDataTable, 
      schemas[0], baselineVersion, baselineDescription, flywayCallbacks).baseline();
    }
  }
  
  //Data migration
  DbMigrate dbMigrate = new DbMigrate(connectionUserObjects, dbSupport, 
    metaDataTable,schemas[0], migrationResolver, ignoreFailedFutureMigration, 
    Flyway.this);
  return dbMigrate.migrate();
}

Next, look at the code snippet of DbMigrate. migrate ().

MigrationInfoServiceImplComparing ResolvedMigration with AppliedMigration objects, find out the need to execute database migration scripts, and return through pending () method. Finally, the database migration script is executed.

public int migrate() {
  int migrationSuccessCount = 0;
  while (true) {
    int count = metaDataTable.lock(new Callable<Integer>() {
                
      @Override
      public Integer call() {
        //To simplify the code, ignore parameter passing
        return doMigrate();
      }
    }
    if (count == 0) {
      // No further migrations available
      break;
    }
    migrationSuccessCount += count;
  }
  return migrationSuccessCount;
}

private int doMigrate() {
  //Collect database migration records already in the database and database migration scripts in file form
  MigrationInfoServiceImpl infoService = new MigrationInfoServiceImpl(
    migrationResolver, metaDataTable, configuration.getTarget(), 
    configuration.isOutOfOrder(), true, true, true);
  infoService.refresh();
  
  //infoService.pending()Record database migration scripts to be executed
  LinkedHashMap<MigrationInfoImpl, Boolean> group = 
    groupMigrationInfo(infoService.pending());
  if (!group.isEmpty()) {
    //Perform database migration operations
    applyMigrations(group);
  }
}

DbMigrate.doMigrateGroup() Execute database migration scripts

private void doMigrateGroup() {
  //Execute migration script
  migration.getResolvedMigration().getExecutor().execute(connectionUserObjects);
  
  //Store in database
  AppliedMigration appliedMigration = new AppliedMigration(migration.getVersion(), 
    migration.getDescription(), migration.getType(), migration.getScript(), 
    migration.getResolvedMigration().getChecksum(), executionTime, true);
  metaDataTable.addAppliedMigration(appliedMigration);
}

Extension exercises

Introducing Flyway framework into an online game project. For existing game clothes, only new SQL statements are executed. For newly built game clothes, database needs to be created and new SQL statements are executed.

In this requirement, what we need to do is to create a database for the newly built game clothes. We can do this through Flyway Callback, which executes the statement to initialize the database if the specified database is empty.

The code is as follows:

public static void main(String[] args) {
    final Flyway flyway = new Flyway();
    flyway.setDataSource("jdbc:mysql://localhost/test", "root", "root");
    
    FlywayCallback flywayCallback = new BaseFlywayCallback() {
        
        @Override
        public void beforeMigrate(Connection connection) {
            DbSupport dbSupport = DbSupportFactory.createDbSupport(connection, false);
            if(!hasTable(dbSupport)) {
                initDb(dbSupport);
            }
        }

        private boolean hasTable(DbSupport dbSupport) {
            Schema<?> schema = dbSupport.getOriginalSchema();
            Table[] tables = schema.allTables();
            if(tables == null || tables.length == 0) {
                return false;
            }
            //Ignore the table schema_version
            if(tables.length == 1 && 
                tables[0].getName().equalsIgnoreCase("schema_version")) {
                return false;
            }
            return true;
        }
        

        private  void initDb(DbSupport dbSupport) {
            Scanner scanner = new Scanner(
              Thread.currentThread().getContextClassLoader());
            Resource[] resources = scanner.scanForResources(
              new Location("db/init"), "", ".sql");
            if(resources == null || resources.length == 0) {
                throw new RuntimeException(
                  "db/init/*.sql not found in the classpath. ");
            }
            for(Resource resource : resources) {
              SqlMigrationExecutor executor = new SqlMigrationExecutor(
                  dbSupport, resource, PlaceholderReplacer.NO_PLACEHOLDERS, flyway);
              executor.execute(dbSupport.getJdbcTemplate().getConnection());
            }
        }
    };
    
    List<FlywayCallback> callbacks = new ArrayList<FlywayCallback>(
      Arrays.asList(flyway.getCallbacks()));
    callbacks.add(flywayCallback);
    flyway.setCallbacks(callbacks.toArray(new FlywayCallback[callbacks.size()]));
    
    flyway.setBaselineOnMigrate(true);
    flyway.repair();
    flyway.migrate();
    
}

Leave a Reply

Your email address will not be published. Required fields are marked *