(fix) Datenbank-Setup Umbau zu SCS
This commit is contained in:
@@ -22,6 +22,16 @@ kotlin {
|
||||
implementation(libs.kotlin.test)
|
||||
}
|
||||
|
||||
jvmMain.dependencies {
|
||||
// Datenbankabhängigkeiten
|
||||
implementation("com.zaxxer:HikariCP:5.0.1")
|
||||
implementation(libs.exposed.core)
|
||||
implementation(libs.exposed.dao)
|
||||
implementation(libs.exposed.jdbc)
|
||||
implementation(libs.exposed.kotlinDatetime)
|
||||
implementation(libs.postgresql.driver)
|
||||
}
|
||||
|
||||
jsMain.dependencies {
|
||||
// Kotlin React dependencies with explicit stable versions (for shared components)
|
||||
implementation("org.jetbrains.kotlin-wrappers:kotlin-react:18.2.0-pre.467")
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package at.mocode.shared.database
|
||||
|
||||
/**
|
||||
* Konfiguration für die Datenbankverbindung.
|
||||
* Parameter werden aus Umgebungsvariablen gelesen oder Standardwerte verwendet.
|
||||
*/
|
||||
data class DatabaseConfig(
|
||||
val jdbcUrl: String,
|
||||
val username: String,
|
||||
val password: String,
|
||||
val driverClassName: String = "org.postgresql.Driver",
|
||||
val maxPoolSize: Int = 10
|
||||
) {
|
||||
companion object {
|
||||
/**
|
||||
* Erstellt eine Datenbank-Konfiguration aus Umgebungsvariablen.
|
||||
* Wenn keine Umgebungsvariablen gefunden werden, werden Standardwerte für die Entwicklung verwendet.
|
||||
*/
|
||||
fun fromEnv(): DatabaseConfig {
|
||||
val host = System.getenv("DB_HOST") ?: "localhost"
|
||||
val port = System.getenv("DB_PORT") ?: "5432"
|
||||
val database = System.getenv("DB_NAME") ?: "meldestelle_db"
|
||||
val username = System.getenv("DB_USER") ?: "meldestelle_user"
|
||||
val password = System.getenv("DB_PASSWORD") ?: "secure_password_change_me"
|
||||
|
||||
return DatabaseConfig(
|
||||
jdbcUrl = "jdbc:postgresql://$host:$port/$database",
|
||||
username = username,
|
||||
password = password
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package at.mocode.shared.database
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig
|
||||
import com.zaxxer.hikari.HikariDataSource
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import org.jetbrains.exposed.sql.Database
|
||||
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
|
||||
|
||||
/**
|
||||
* Factory-Klasse für die Datenbankverbindung.
|
||||
* Stellt eine Verbindung zur Datenbank her und konfiguriert den Connection Pool.
|
||||
*/
|
||||
object DatabaseFactory {
|
||||
private var dataSource: HikariDataSource? = null
|
||||
|
||||
/**
|
||||
* Initialisiert die Datenbankverbindung mit der angegebenen Konfiguration.
|
||||
* @param config Die Datenbankkonfiguration
|
||||
*/
|
||||
fun init(config: DatabaseConfig) {
|
||||
if (dataSource != null) {
|
||||
close()
|
||||
}
|
||||
|
||||
val hikariConfig = HikariConfig().apply {
|
||||
driverClassName = config.driverClassName
|
||||
jdbcUrl = config.jdbcUrl
|
||||
username = config.username
|
||||
password = config.password
|
||||
maximumPoolSize = config.maxPoolSize
|
||||
isAutoCommit = false
|
||||
transactionIsolation = "TRANSACTION_REPEATABLE_READ"
|
||||
validate()
|
||||
}
|
||||
|
||||
dataSource = HikariDataSource(hikariConfig)
|
||||
Database.connect(dataSource!!)
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt eine Datenbankoperation in einer Transaktion aus.
|
||||
* @param block Der Code, der in der Transaktion ausgeführt werden soll
|
||||
* @return Das Ergebnis der Transaktion
|
||||
*/
|
||||
suspend fun <T> dbQuery(block: suspend () -> T): T =
|
||||
newSuspendedTransaction(Dispatchers.IO) { block() }
|
||||
|
||||
/**
|
||||
* Schließt die Datenbankverbindung.
|
||||
*/
|
||||
fun close() {
|
||||
dataSource?.close()
|
||||
dataSource = null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package at.mocode.shared.database
|
||||
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import org.jetbrains.exposed.sql.transactions.transaction
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.CurrentTimestamp
|
||||
import org.jetbrains.exposed.sql.kotlin.datetime.timestamp
|
||||
|
||||
/**
|
||||
* Führt Datenbankmigrationen durch.
|
||||
* Diese Klasse verwaltet und führt alle notwendigen Datenbankmigrationen aus.
|
||||
*/
|
||||
object DatabaseMigrator {
|
||||
private val migrations = mutableListOf<Migration>()
|
||||
private val executedMigrations = mutableSetOf<String>()
|
||||
|
||||
/**
|
||||
* Registriert eine Migration.
|
||||
* @param migration Die zu registrierende Migration
|
||||
*/
|
||||
fun register(migration: Migration) {
|
||||
migrations.add(migration)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registriert mehrere Migrationen auf einmal.
|
||||
* @param migrations Die zu registrierenden Migrationen
|
||||
*/
|
||||
fun registerAll(vararg migrations: Migration) {
|
||||
this.migrations.addAll(migrations)
|
||||
}
|
||||
|
||||
/**
|
||||
* Führt alle registrierten Migrationen aus, die noch nicht ausgeführt wurden.
|
||||
*/
|
||||
fun migrate() {
|
||||
// Erstelle die Migrationstabelle, wenn sie nicht existiert
|
||||
transaction {
|
||||
SchemaUtils.create(MigrationTable)
|
||||
|
||||
// Lade bereits ausgeführte Migrationen
|
||||
MigrationTable.selectAll().forEach {
|
||||
executedMigrations.add(it[MigrationTable.id])
|
||||
}
|
||||
|
||||
// Sortiere Migrationen nach Version
|
||||
val sortedMigrations = migrations.sortedBy { it.version }
|
||||
|
||||
// Führe noch nicht ausgeführte Migrationen aus
|
||||
for (migration in sortedMigrations) {
|
||||
if (!executedMigrations.contains(migration.id)) {
|
||||
println("Ausführen der Migration: ${migration.id}")
|
||||
try {
|
||||
migration.up()
|
||||
|
||||
// Markiere Migration als ausgeführt
|
||||
MigrationTable.insert {
|
||||
it[id] = migration.id
|
||||
it[version] = migration.version
|
||||
it[description] = migration.description
|
||||
}
|
||||
|
||||
commit()
|
||||
println("Migration erfolgreich: ${migration.id}")
|
||||
} catch (e: Exception) {
|
||||
rollback()
|
||||
println("Migration fehlgeschlagen: ${migration.id} - ${e.message}")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tabelle zur Verfolgung ausgeführter Migrationen.
|
||||
*/
|
||||
object MigrationTable : Table("_migrations") {
|
||||
val id = varchar("id", 100)
|
||||
val version = long("version")
|
||||
val description = varchar("description", 255)
|
||||
val executedAt = timestamp("executed_at").defaultExpression(CurrentTimestamp)
|
||||
|
||||
override val primaryKey = PrimaryKey(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Basisklasse für Datenbankmigrationen.
|
||||
*/
|
||||
abstract class Migration(val version: Long, val description: String) {
|
||||
/**
|
||||
* Eindeutige ID der Migration, bestehend aus Version und Beschreibung.
|
||||
*/
|
||||
val id: String = "V${version}_${description.replace("\\s+".toRegex(), "_")}"
|
||||
|
||||
/**
|
||||
* Führt die Migration aus.
|
||||
*/
|
||||
abstract fun up()
|
||||
}
|
||||
Reference in New Issue
Block a user