refactor: Integrate Flyway for database migrations and simplify Docker Compose and tenant configurations.
All checks were successful
Build and Push Docker Images / build-and-push-backend (push) Successful in 3m44s
Build and Push Docker Images / build-and-push-frontend (push) Successful in 11s
Build and Push Docker Images / deploy-to-k8s (push) Successful in 1m19s

This commit is contained in:
Zuev
2026-03-13 04:35:50 +03:00
parent 10c06e726a
commit 9f124c52a5
10 changed files with 114 additions and 278 deletions

View File

@@ -2,10 +2,11 @@ package com.magistr.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, FlywayAutoConfiguration.class})
@EnableScheduling
public class Application {

View File

@@ -120,46 +120,37 @@ public class TenantConfigWatcher {
}
/**
* Инициализирует БД нового тенанта: проверяет наличие таблиц,
* если нет — выполняет init.sql.
* Выполняет миграции Flyway для конкретного тенанта пи подключении.
* Если БД уже существует, но история Flyway пуста —
* делает baseline (считает V1_init.sql уже выполненным).
*/
public void initDatabaseForTenant(TenantConfig tenant) {
String domain = tenant.getDomain();
try {
TenantContext.setCurrentTenant(domain);
if (needsInit()) {
log.info("[{}] Tables not found — executing init.sql...", domain);
executeInitSql();
log.info("[{}] init.sql executed successfully", domain);
} else {
log.info("[{}] Tables already exist, skipping init", domain);
log.info("[{}] Starting Flyway migrations...", domain);
// Получаем DataSource конкретно для этого тенанта
javax.sql.DataSource tenantDs = routingDataSource.getResolvedDataSources().get(domain);
if (tenantDs == null) {
// Если ещё не resolve'нулся (первый запуск), берём обёртку
tenantDs = dataSource;
}
org.flywaydb.core.Flyway flyway = org.flywaydb.core.Flyway.configure()
.dataSource(tenantDs)
.baselineOnMigrate(true)
.baselineVersion("1")
.load();
flyway.migrate();
log.info("[{}] Flyway migrations completed successfully", domain);
} catch (Exception e) {
log.error("[{}] DB init failed: {}", domain, e.getMessage());
log.error("[{}] Flyway migration failed: {}", domain, e.getMessage());
} finally {
TenantContext.clear();
}
}
private boolean needsInit() {
try (Connection conn = dataSource.getConnection();
ResultSet rs = conn.getMetaData().getTables(null, null, "users", new String[]{"TABLE"})) {
return !rs.next();
} catch (Exception e) {
return true;
}
}
private void executeInitSql() throws Exception {
String sql;
try (InputStream is = new ClassPathResource("init.sql").getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
sql = reader.lines().collect(Collectors.joining("\n"));
}
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement()) {
stmt.execute(sql);
}
}
}

View File

@@ -101,12 +101,12 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
em.setPackagesToScan("com.magistr.app.model");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setGenerateDdl(false);
vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
em.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> props = new HashMap<>();
props.put("hibernate.hbm2ddl.auto", "update");
props.put("hibernate.hbm2ddl.auto", "none");
props.put("hibernate.show_sql", "false");
em.setJpaPropertyMap(props);