Heim > Artikel > Backend-Entwicklung > So verwenden Sie Migrationen mit Golang
Einfache Beispielanwendung, die die Verwendung von golang-migrate zeigt
Viele Leute stellen diese Frage und ich habe versucht, diese Liste zu erstellen, um die wichtigsten Vorteile der Verwendung von Migrationen hervorzuheben:
Versionskontrolle: Eine der wichtigsten und wichtigsten ist die Möglichkeit, eine Versionierung der verschiedenen Änderungen des Datenbankschemas zu ermöglichen. Ohne die Migrationen wären diese Schemaänderungen inkohärent und könnten nicht nachverfolgt werden, was zu Versionierungsproblemen und möglichen Fehlern führen würde.
Rollback: Für den Fall eines Fehlers ist immer ein Rollback-System erforderlich. Ein Migrationssystem verfügt immer über zwei Methoden: oben, um die Änderungen in der Datenbank anzuwenden, und unten, um die Änderungen schnell und konsistent rückgängig zu machen :-)
Automatisierung und CI/CD-Integration: Migrationen können automatisiert werden, sodass sie Teil der CI/CD-Pipeline sein können. Dies hilft bei der reibungslosen und konsistenten Bereitstellung von Änderungen ohne manuellen Eingriff.
Wir können noch viele weitere Vorteile finden, aber ich denke, diese Punkte stellen eine gute Zusammenfassung der Hauptvorteile dar.
Go unterstützt Migrationen aus diesem Grund nicht nativ, daher können wir das beliebte Paket golang-migrate verwenden. Auch wenn Sie ein ORM wie GORM verwenden, können Sie es dafür verwenden.
Beide Pakete sind sehr beliebt, aber in diesem Beispiel verwende ich golang-migrate, da ich kein Interesse an der Implementierung eines ORM habe.
Sehen wir uns Schritt für Schritt an, wie eine einfache Anwendung implementiert wird, um zu sehen, wie sie verwendet wird.
Um diesem Artikel folgen zu können, benötigen Sie: Go und Docker mit Docker Compose
Erstellen Sie die Datei docker-compose.yml in Ihrem Stammverzeichnis, in dem wir Ihre Lieblings-DB definieren. In meinem Fall verwenden Sie eine MariaDB, Sie können aber auch gerne eine andere verwenden.
services: mariadb: image: mariadb:11.5.2 container_name: mariadb_example_go_migration ports: - "3306:3306" environment: - MYSQL_DATABASE=app - MYSQL_ROOT_PASSWORD=root - TZ=Europe/Berlin volumes: - mariadbdata:/var/lib/mysql volumes: mariadbdata: driver: local docker compose up -d
Wenn Sie möchten, können Sie Docker direkt anstelle von Docker-Compose verwenden:
docker volume create -d local mariadbdata docker run --name mariadb_example_go_migration -p 3306:3306 -e MYSQL_DATABASE=app -e MYSQL_ROOT_PASSWORD=root -e TZ=Europe/Berlin -v mariadbdata:/var/lib/mysql mariadb:11.5.2
Erstellen oder aktualisieren Sie die Datei .env in Ihrem Stammverzeichnis, in dem Sie die Variablen definieren müssen, um unsere Datenbank zu verbinden.
DATABASE_DSN=root:root@tcp(localhost:3306)/app
Erstellen Sie eine einfache Golang-Anwendung, um eine erfolgreiche DB-Verbindung sicherzustellen und alle Tabellen und Strukturen in der Datenbank mit ihrer Struktur aufzulisten. cmd/main.go
package main import ( "database/sql" "fmt" "log" "os" "text/tabwriter" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { // Load .env variables err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Open connection with MySQL DB db, err := sql.Open("mysql", os.Getenv("DATABASE_DSN")) if err != nil { log.Fatalf("Error opening database: %v\n", err) } defer db.Close() // Ensure that the connection works err = db.Ping() if err != nil { log.Fatalf("Error connecting database: %v\n", err) } fmt.Println("Connected to database") // Execute the SHOW TABLES query to list all tables in the database tables, err := db.Query("SHOW TABLES") if err != nil { log.Fatalf("Failed to execute SHOW TABLES query: %v\n", err) } defer tables.Close() fmt.Println("Database structure:") for tables.Next() { var tableName string if err := tables.Scan(&tableName); err != nil { log.Fatalf("Failed to scan table name: %v\n", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug) fmt.Printf("\n[Table: %s]\n\n", tableName) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "Field", "Type", "Null", "Key", "Default", "Extra") // Get the structure of the current table structureQuery := fmt.Sprintf("DESCRIBE %s", tableName) columns, err := db.Query(structureQuery) if err != nil { log.Fatalf("Failed to describe table %s: %v\n", tableName, err) } defer columns.Close() for columns.Next() { var field, colType, null, key, defaultVal, extra sql.NullString err := columns.Scan(&field, &colType, &null, &key, &defaultVal, &extra) if err != nil { log.Fatalf("Failed to scan column: %v\n", err) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", field.String, colType.String, null.String, key.String, defaultVal.String, extra.String) } w.Flush() } }
Und wenn wir es ausführen, erhalten wir eine ähnliche Ausgabe:
Um die Golang-Migrate-CLI auszuführen, haben Sie grundsätzlich zwei Methoden: Installieren Sie die CLI lokal oder führen Sie sie über das offizielle Docker-Image aus: migrieren/migrieren.
Persönlich bevorzuge ich die Docker-Variante, aber in diesem Tutorial werden beide Varianten veranschaulicht.
Der erste Schritt besteht darin, mit dem nächsten Befehl eine leere Migration zu erstellen.
services: mariadb: image: mariadb:11.5.2 container_name: mariadb_example_go_migration ports: - "3306:3306" environment: - MYSQL_DATABASE=app - MYSQL_ROOT_PASSWORD=root - TZ=Europe/Berlin volumes: - mariadbdata:/var/lib/mysql volumes: mariadbdata: driver: local docker compose up -d
docker volume create -d local mariadbdata docker run --name mariadb_example_go_migration -p 3306:3306 -e MYSQL_DATABASE=app -e MYSQL_ROOT_PASSWORD=root -e TZ=Europe/Berlin -v mariadbdata:/var/lib/mysql mariadb:11.5.2
Dieser Befehl generiert zwei leere Dateien im Ordner „database/migrations/“: 000001createuserstable.up.sql und 000001createuserstable.down.sql
In der Datei 000001createuserstable.up.sql definieren Sie SQL zum Erstellen einer Tabelle für Benutzer:
DATABASE_DSN=root:root@tcp(localhost:3306)/app
In der Datei 000001createuserstable.down.sql definieren Sie SQL, um alle von up vorgenommenen Änderungen rückgängig zu machen. In diesem Fall müssen wir die Benutzertabelle löschen:
package main import ( "database/sql" "fmt" "log" "os" "text/tabwriter" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { // Load .env variables err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Open connection with MySQL DB db, err := sql.Open("mysql", os.Getenv("DATABASE_DSN")) if err != nil { log.Fatalf("Error opening database: %v\n", err) } defer db.Close() // Ensure that the connection works err = db.Ping() if err != nil { log.Fatalf("Error connecting database: %v\n", err) } fmt.Println("Connected to database") // Execute the SHOW TABLES query to list all tables in the database tables, err := db.Query("SHOW TABLES") if err != nil { log.Fatalf("Failed to execute SHOW TABLES query: %v\n", err) } defer tables.Close() fmt.Println("Database structure:") for tables.Next() { var tableName string if err := tables.Scan(&tableName); err != nil { log.Fatalf("Failed to scan table name: %v\n", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug) fmt.Printf("\n[Table: %s]\n\n", tableName) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "Field", "Type", "Null", "Key", "Default", "Extra") // Get the structure of the current table structureQuery := fmt.Sprintf("DESCRIBE %s", tableName) columns, err := db.Query(structureQuery) if err != nil { log.Fatalf("Failed to describe table %s: %v\n", tableName, err) } defer columns.Close() for columns.Next() { var field, colType, null, key, defaultVal, extra sql.NullString err := columns.Scan(&field, &colType, &null, &key, &defaultVal, &extra) if err != nil { log.Fatalf("Failed to scan column: %v\n", err) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", field.String, colType.String, null.String, key.String, defaultVal.String, extra.String) } w.Flush() } }
Der folgende Befehl wendet alle ausstehenden Migrationen an. Sie können auch die Anzahl der anzuwendenden Migrationen definieren, indem Sie die Zahl nach dem Up hinzufügen.
#CLI variant migrate create -ext sql -dir ./database/migrations -seq create_users_table
#Docker CLI variant docker run --rm -v $(pwd)/database/migrations:/migrations migrate/migrate \ create -ext sql -dir /migrations -seq create_users_table
HINWEIS: Wenn die Migration zum ersten Mal ausgeführt wird, wird eine Tabelle „schema_migrations“ erstellt, in der die Migration die angewendete Versionsnummer kennt.
Und führen Sie unsere Golang-Anwendung aus, um die Ergebnisse anzuzeigen:
Fügen Sie ein neues Spaltentelefon zur Benutzertabelle hinzu
CREATE TABLE `users` ( `id` VARCHAR(36) NOT NULL PRIMARY KEY, `name` VARCHAR(255) NOT NULL, `email` VARCHAR(255) NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL );
DROP TABLE IF EXISTS `users`;
#CLI variant migrate -path=./database/migrations -database "mysql://root:root@tcp(localhost:3306)/app" up
#Docker CLI variant docker run --rm -v $(pwd)/database/migrations:/migrations --network host migrate/migrate \ -path=/migrations -database "mysql://root:root@tcp(localhost:3306)/app" up
Und wenn Sie es über unsere Golang-Anwendung ausführen, sehen Sie das neue Feld:
Mit dem folgenden Befehl können wir die angewendeten Daten ganz einfach zurücksetzen. Migrationen. Im folgenden Beispiel können wir sehen, wie wir die zuletzt angewendete Migration rückgängig machen:
#CLI variant migrate create -ext sql -dir ./database/migrations -seq add_column_phone #Docker CLI variant docker run --rm -v $(pwd)/database/migrations:/migrations migrate/migrate \ create -ext sql -dir /migrations -seq add_column_phone
-- 000002_add_column_phone.up.sql ALTER TABLE `users` ADD `phone` VARCHAR(255) NULL;
WARNUNG: Wenn Sie die Anzahl der Migrationen nicht definieren, wird ROLLBACK auf ALLE MIGRATIONEN angewendet!
Und dann können wir zeigen, dass die letzte Migration rückgängig gemacht und das Telefonfeld entfernt wurde :-)
Wenn eine Migration Fehler enthält und ausgeführt wird, kann diese Migration nicht angewendet werden und das Migrationssystem verhindert weitere Migrationen in der Datenbank, bis diese Migration behoben ist.
Und wenn wir versuchen, uns zu bewerben, erhalten wir eine Nachricht wie diese:
services: mariadb: image: mariadb:11.5.2 container_name: mariadb_example_go_migration ports: - "3306:3306" environment: - MYSQL_DATABASE=app - MYSQL_ROOT_PASSWORD=root - TZ=Europe/Berlin volumes: - mariadbdata:/var/lib/mysql volumes: mariadbdata: driver: local docker compose up -d
Keine Panik, es ist nicht schwer, zu einem einheitlichen System zurückzukehren.
Zuerst müssen wir die beschädigte Migration beheben, in diesem Fall Version 2.
Sobald die Migration gelöst ist, müssen wir das System auf die letzte gültige Version zwingen, in diesem Fall Version 1.
docker volume create -d local mariadbdata docker run --name mariadb_example_go_migration -p 3306:3306 -e MYSQL_DATABASE=app -e MYSQL_ROOT_PASSWORD=root -e TZ=Europe/Berlin -v mariadbdata:/var/lib/mysql mariadb:11.5.2
DATABASE_DSN=root:root@tcp(localhost:3306)/app
Und jetzt können Sie die Migrationen problemlos erneut anwenden ;-)
Um unsere Produktivität zu verbessern und die Verwendung dieser Befehle zu erleichtern, können wir Makefile verwenden. Unten sehen Sie die beiden Varianten: Native Client und Docker.
CLI-Variante
package main import ( "database/sql" "fmt" "log" "os" "text/tabwriter" _ "github.com/go-sql-driver/mysql" "github.com/joho/godotenv" ) func main() { // Load .env variables err := godotenv.Load() if err != nil { log.Fatal("Error loading .env file") } // Open connection with MySQL DB db, err := sql.Open("mysql", os.Getenv("DATABASE_DSN")) if err != nil { log.Fatalf("Error opening database: %v\n", err) } defer db.Close() // Ensure that the connection works err = db.Ping() if err != nil { log.Fatalf("Error connecting database: %v\n", err) } fmt.Println("Connected to database") // Execute the SHOW TABLES query to list all tables in the database tables, err := db.Query("SHOW TABLES") if err != nil { log.Fatalf("Failed to execute SHOW TABLES query: %v\n", err) } defer tables.Close() fmt.Println("Database structure:") for tables.Next() { var tableName string if err := tables.Scan(&tableName); err != nil { log.Fatalf("Failed to scan table name: %v\n", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', tabwriter.Debug) fmt.Printf("\n[Table: %s]\n\n", tableName) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", "Field", "Type", "Null", "Key", "Default", "Extra") // Get the structure of the current table structureQuery := fmt.Sprintf("DESCRIBE %s", tableName) columns, err := db.Query(structureQuery) if err != nil { log.Fatalf("Failed to describe table %s: %v\n", tableName, err) } defer columns.Close() for columns.Next() { var field, colType, null, key, defaultVal, extra sql.NullString err := columns.Scan(&field, &colType, &null, &key, &defaultVal, &extra) if err != nil { log.Fatalf("Failed to scan column: %v\n", err) } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n", field.String, colType.String, null.String, key.String, defaultVal.String, extra.String) } w.Flush() } }
Docker-CLI-Variante
#CLI variant migrate create -ext sql -dir ./database/migrations -seq create_users_table
Der Code für dieses Tutorial ist öffentlich zu finden: GitHub – albertcolom/example-go-migration
Original veröffentlicht unter: albertcolom.com
Das obige ist der detaillierte Inhalt vonSo verwenden Sie Migrationen mit Golang. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!