Skip to content

Instantly share code, notes, and snippets.

@gregorriegler
Last active June 15, 2023 06:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregorriegler/85a1d51daba957ddc1c344dae8aa3aee to your computer and use it in GitHub Desktop.
Save gregorriegler/85a1d51daba957ddc1c344dae8aa3aee to your computer and use it in GitHub Desktop.
Generates Flyway Migration Scripts Automatically using Hibernate Schema Updater and Testcontainers
package org.acme;
import com.microsoft.sqlserver.jdbc.SQLServerDriver;
import org.flywaydb.core.Flyway;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import org.hibernate.tool.schema.TargetType;
import org.reflections.Reflections;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.testcontainers.containers.MSSQLServerContainer;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
/**
* Writes suggested Database Migration Scripts according
* to changes in your Hibernate Entities to target/ddl_schema/update.sql
*
* Uses Hibernate DDL Schema Generator, Testcontainers,
* your existing Migrations and Entities to do so.
*
* Add testcontainers as a compile time dependency like so:
* <dependency>
* <groupId>org.testcontainers</groupId>
* <artifactId>testcontainers</artifactId>
* <version>1.14.2</version>
* <scope>compile</scope>
* </dependency>
* <dependency>
* <groupId>org.testcontainers</groupId>
* <artifactId>mssqlserver</artifactId>
* <version>1.14.2</version>
* <scope>compile</scope>
* </dependency>
*
* And configure your environment in the main method
* using the SuggestDbMigrationBuilder
*/
public class SuggestFlywayMigration {
private final String dialect;
private final String physicalNamingStrategy;
private final String implicitNamingStrategy;
private final List<String> packages;
private final String outputFile;
public SuggestFlywayMigration(String dialect, String physicalNamingStrategy, String implicitNamingStrategy, List<String> packages, String outputFile) {
this.dialect = dialect;
this.physicalNamingStrategy = physicalNamingStrategy;
this.implicitNamingStrategy = implicitNamingStrategy;
this.packages = packages;
this.outputFile = outputFile;
}
public static void main(String[] args) {
new SuggestFlywayMigrationBuilder()
.dialect("org.hibernate.dialect.SQLServer2012Dialect")
.physicalNamingStrategy("org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy")
.implicitNamingStrategy("org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy")
.packages(Collections.singletonList("com.package"))
.outputFile("target/ddl_schema/update.sql")
.build()
.suggestMigration();
}
private void suggestMigration() {
MSSQLServerContainer sqlServer = startSqlServer();
flywayMigrate(sqlServer);
outputHibernateAutomatedUpdate(sqlServer);
sqlServer.stop();
}
private MSSQLServerContainer startSqlServer() {
MSSQLServerContainer mssqlServerContainerFlyway = new MSSQLServerContainer();
mssqlServerContainerFlyway.start();
return mssqlServerContainerFlyway;
}
private void flywayMigrate(MSSQLServerContainer sqlServer) {
Flyway flyway = new Flyway();
flyway.setDataSource(createDataSource(sqlServer));
flyway.migrate();
}
private SimpleDriverDataSource createDataSource(MSSQLServerContainer sqlServer) {
return new SimpleDriverDataSource(
new SQLServerDriver(),
sqlServer.getJdbcUrl(),
sqlServer.getUsername(),
sqlServer.getPassword()
);
}
private void outputHibernateAutomatedUpdate(MSSQLServerContainer sqlServer) {
MetadataSources metadata = new MetadataSources(
new StandardServiceRegistryBuilder()
.applySetting("hibernate.dialect", dialect)
.applySetting("hibernate.connection.driver_class", sqlServer.getDriverClassName())
.applySetting("hibernate.connection.url", sqlServer.getJdbcUrl())
.applySetting("hibernate.connection.username", sqlServer.getUsername())
.applySetting("hibernate.connection.password", sqlServer.getPassword())
.applySetting("hibernate.physical_naming_strategy", physicalNamingStrategy)
.applySetting("hibernate.implicit_naming_strategy", implicitNamingStrategy)
.build());
addEntitiesToMetadata(metadata);
SchemaUpdate schemaUpdate = new SchemaUpdate();
schemaUpdate.setOutputFile(outputFile);
schemaUpdate.setDelimiter(";");
schemaUpdate.execute(EnumSet.of(TargetType.SCRIPT), metadata.buildMetadata());
}
private void addEntitiesToMetadata(MetadataSources metadata) {
for (String entityPackage : packages) {
final Reflections reflections = new Reflections(entityPackage);
for (Class<?> cl : reflections.getTypesAnnotatedWith(MappedSuperclass.class)) {
metadata.addAnnotatedClass(cl);
}
for (Class<?> cl : reflections.getTypesAnnotatedWith(Entity.class)) {
metadata.addAnnotatedClass(cl);
}
}
}
public static class SuggestFlywayMigrationBuilder {
private String dialect;
private String physicalNamingStrategy;
private String implicitNamingStrategy;
private List<String> packages;
private String outputFile;
public SuggestFlywayMigrationBuilder dialect(String dialect) {
this.dialect = dialect;
return this;
}
public SuggestFlywayMigrationBuilder physicalNamingStrategy(String physicalNamingStrategy) {
this.physicalNamingStrategy = physicalNamingStrategy;
return this;
}
public SuggestFlywayMigrationBuilder implicitNamingStrategy(String implicitNamingStrategy) {
this.implicitNamingStrategy = implicitNamingStrategy;
return this;
}
public SuggestFlywayMigrationBuilder packages(List<String> packages) {
this.packages = packages;
return this;
}
public SuggestFlywayMigrationBuilder outputFile(String outputFile) {
this.outputFile = outputFile;
return this;
}
public SuggestFlywayMigration build() {
return new SuggestFlywayMigration(dialect, physicalNamingStrategy, implicitNamingStrategy, packages, outputFile);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment