Last active
June 15, 2023 06:55
-
-
Save gregorriegler/85a1d51daba957ddc1c344dae8aa3aee to your computer and use it in GitHub Desktop.
Generates Flyway Migration Scripts Automatically using Hibernate Schema Updater and Testcontainers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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