Fix schema init: bypass DataSource, use direct JDBC with qualified table names
All checks were successful
CI / build (push) Successful in 48s
CI / docker (push) Successful in 40s
CI / deploy (push) Successful in 7s

The auto-configured DataSource targets jdbc:ch://.../cameleer3 which fails
if the database doesn't exist yet. Schema init now uses a direct JDBC
connection to the root URL, creates the database first, then applies all
schema SQL with fully qualified cameleer3.* table names.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-12 21:50:47 +01:00
parent 48bdb46760
commit ce0eb58b0c
3 changed files with 25 additions and 34 deletions

View File

@@ -20,6 +20,10 @@ import java.sql.Statement;
* <p>
* Spring Boot auto-configures the DataSource from {@code spring.datasource.*} properties.
* This class exposes a JdbcTemplate bean and initializes the schema on startup.
* <p>
* Schema initialization uses a direct JDBC connection (bypassing the DataSource)
* to avoid chicken-and-egg problems: the DataSource targets a specific database
* that may not exist yet on a fresh ClickHouse instance.
*/
@Configuration
public class ClickHouseConfig {
@@ -49,43 +53,30 @@ public class ClickHouseConfig {
@PostConstruct
void initSchema() {
ensureDatabaseExists();
var jdbc = new JdbcTemplate(dataSource);
for (String schemaFile : SCHEMA_FILES) {
try {
String dbName = extractDatabaseName(datasourceUrl);
String rootUrl = datasourceUrl.replaceFirst("/[^/?]+($|\\?)", "/$1");
try (Connection conn = DriverManager.getConnection(rootUrl, datasourceUsername, datasourcePassword);
Statement stmt = conn.createStatement()) {
// Create the database first
stmt.execute("CREATE DATABASE IF NOT EXISTS " + dbName);
log.info("Ensured database '{}' exists", dbName);
// Apply schema files using fully qualified table names
for (String schemaFile : SCHEMA_FILES) {
String sql = new ClassPathResource(schemaFile).getContentAsString(StandardCharsets.UTF_8);
for (String statement : sql.split(";")) {
String trimmed = statement.trim();
if (!trimmed.isEmpty() && !trimmed.startsWith("--")) {
jdbc.execute(trimmed);
stmt.execute(trimmed);
}
}
log.info("Applied schema: {}", schemaFile);
} catch (Exception e) {
log.error("Failed to apply schema: {}", schemaFile, e);
throw new RuntimeException("Schema initialization failed: " + schemaFile, e);
}
}
}
/**
* Creates the ClickHouse database if it doesn't exist.
* Uses a separate connection without the database path, since the main
* DataSource connection fails if the database doesn't exist yet.
*/
private void ensureDatabaseExists() {
// Extract database name from URL: jdbc:ch://host:port/dbname -> dbname
// Strip the database path to connect at root level
String rootUrl = datasourceUrl.replaceFirst("/[^/?]+($|\\?)", "$1");
String dbName = extractDatabaseName(datasourceUrl);
try (Connection conn = DriverManager.getConnection(rootUrl, datasourceUsername, datasourcePassword);
Statement stmt = conn.createStatement()) {
stmt.execute("CREATE DATABASE IF NOT EXISTS " + dbName);
log.info("Ensured database '{}' exists", dbName);
} catch (Exception e) {
log.error("Failed to ensure database exists", e);
throw new RuntimeException("Database creation failed", e);
log.error("ClickHouse schema initialization failed", e);
throw new RuntimeException("ClickHouse schema initialization failed", e);
}
}