Datenbankmigrationen in C++: Überblick über Tools und Bibliotheken

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Datenbankmigrationen in C++: Schemaverwaltung und Versionierung

In modernen C++-Projekten, die mit relationalen Datenbanken (PostgreSQL, MySQL, SQLite) arbeiten, ist die Unterstützung der Schema-Evolution eine zwingende Praxis. Migrationen ermöglichen es, Änderungen an Tabellenstrukturen, Indizes und Constraints automatisch anzuwenden und dabei die Datenintegrität zwischen verschiedenen Versionen der Anwendung zu wahren. Im Gegensatz zu dynamischen Sprachen verfügt C++ über kein integriertes Tool für Migrationen, aber es gibt mehrere ausgereifte Bibliotheken, die diese Aufgabe lösen.



Grundlegende Anforderungen an eine Migrationsbibliothek für C++

  • Versionierung: Jede Migration muss eine eindeutige Kennung (numerisch oder als Zeitstempel) haben.
  • Rollback-Unterstützung: Möglichkeit, "up"- (Erstellung/Änderung) und "down"- (Löschung/Rollback) Operationen anzuwenden.
  • Integration mit DBMS: Arbeit über Standardtreiber (libpq, sqlite3, MySQL Connector).
  • Transaktionalität: Jede Migration wird in einer eigenen Transaktion zur Gewährleistung der Atomarität ausgeführt.
  • Automatische Anwendung: Kommandozeilen-Dienstprogramm oder API zum Starten beim Anwendungsstart.


Beliebte Tools für Migrationen in C++

1. cpp-migrations (empfohlen)

Eine leichtgewichtige Open-Source-Bibliothek, die PostgreSQL, MySQL und SQLite unterstützt. Migrationen werden als C++-Klassen mit den Methoden up() und down() beschrieben. Die Version wird in der Datei migrations.json festgehalten.

// Beispiel einer Migration für cpp-migrations#include <migration.hpp>

class CreateUsersTable : public Migration {public: void up() override { execute("CREATE TABLE users (" "id SERIAL PRIMARY KEY," "name VARCHAR(100) NOT NULL," "email VARCHAR(255) UNIQUE," "created_at TIMESTAMP DEFAULT NOW()" ")"); }

void down() override { execute("DROP TABLE IF EXISTS users"); }};

REGISTER_MIGRATION(CreateUsersTable, "001_create_users");

Starten von Migrationen über die API:

#include <migration_runner.hpp>

int main() { MigrationRunner runner("postgresql://user:pass@localhost/db"); runner.applyPending(); // wendet alle noch nicht angewendeten Migrationen an return 0;}


2. libpq-basierte Migrationen (selbst geschriebener Ansatz)

Wenn das Projekt bereits libpq (den nativen PostgreSQL-Client) verwendet, kann ein einfacher Migrationsmanager auf Basis von SQL-Dateien implementiert werden. Beispielstruktur:

migrations/├── 001_create_users.sql├── 001_create_users.down.sql├── 002_add_role_column.sql└── 002_add_role_column.down.sql

Ladecode in C++:

#include <pqxx/pqxx>#include <filesystem>#include <fstream>

void applyMigrations(pqxx::connection &conn) { pqxx::work txn(conn); // Erstelle eine Tabelle zur Nachverfolgung angewendeter Migrationen txn.exec("CREATE TABLE IF NOT EXISTS _migrations (" "id SERIAL PRIMARY KEY," "name TEXT UNIQUE," "applied_at TIMESTAMP DEFAULT NOW())"); for (const auto &entry : std::filesystem::directory_iterator("migrations")) { std::string filename = entry.path().filename().string(); if (filename.find(".down.") != std::string::npos) continue; if (filename.find(".sql") == std::string::npos) continue; // Prüfen, ob die Migration bereits angewendet wurde auto check = txn.exec_params("SELECT 1 FROM _migrations WHERE name = $1", filename); if (check.empty()) { std::ifstream file(entry.path()); std::string sql((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); txn.exec(sql); txn.exec_params("INSERT INTO _migrations (name) VALU

Empfehlungen