Heim Backend-Entwicklung Golang So verwenden Sie Migrationen mit Golang

So verwenden Sie Migrationen mit Golang

Nov 09, 2024 am 07:28 AM

Einfache Beispielanwendung, die die Verwendung von golang-migrate zeigt

Warum sollten Sie Migrationen nutzen?

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.

Wie implementiert man Migrationen in Golang?

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.

Zeig mir den Code!

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

Infrastruktur

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 
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Umweltwerte

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Erstellen Sie eine einfache Golang-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()
 }
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Und wenn wir es ausführen, erhalten wir eine ähnliche Ausgabe:

How to use migrations with Golang

CLI migrieren

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.

So generieren Sie eine Migration

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 
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
  • ext: Erweiterung der zu generierenden Datei.
  • dir: Verzeichnis, in dem unsere Migration erstellt wird.
  • seq: Name der Migrationssequenz.

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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()
 }
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

So wenden Sie die Migration an

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
Nach dem Login kopieren
Nach dem Login kopieren
#Docker CLI variant
docker run --rm -v $(pwd)/database/migrations:/migrations migrate/migrate \
    create -ext sql -dir /migrations -seq create_users_table
Nach dem Login kopieren
  • Pfad: Pfad zum Migrationsverzeichnis.
  • Datenbank: Definieren Sie Ihre Datenbank-DSN-Verbindung.

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:

How to use migrations with Golang

Neue Migration hinzufügen

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
);
Nach dem Login kopieren
DROP TABLE IF EXISTS `users`;
Nach dem Login kopieren
#CLI variant
migrate -path=./database/migrations -database "mysql://root:root@tcp(localhost:3306)/app" up
Nach dem Login kopieren
#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
Nach dem Login kopieren

Und wenn Sie es über unsere Golang-Anwendung ausführen, sehen Sie das neue Feld:

How to use migrations with Golang

So machen Sie die Migration rückgängig

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
Nach dem Login kopieren
-- 000002_add_column_phone.up.sql
ALTER TABLE `users` ADD `phone` VARCHAR(255) NULL;
Nach dem Login kopieren

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 :-)

Display table users without phone field

So beheben Sie Migrationsfehler

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 
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

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
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
DATABASE_DSN=root:root@tcp(localhost:3306)/app
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Und jetzt können Sie die Migrationen problemlos erneut anwenden ;-)

Makefile

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()
 }
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Docker-CLI-Variante

#CLI variant
migrate create -ext sql -dir ./database/migrations -seq create_users_table
Nach dem Login kopieren
Nach dem Login kopieren

Repository

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!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
2 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Repo: Wie man Teamkollegen wiederbelebt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Abenteuer: Wie man riesige Samen bekommt
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

GO Language Pack Import: Was ist der Unterschied zwischen Unterstrich und ohne Unterstrich? GO Language Pack Import: Was ist der Unterschied zwischen Unterstrich und ohne Unterstrich? Mar 03, 2025 pm 05:17 PM

GO Language Pack Import: Was ist der Unterschied zwischen Unterstrich und ohne Unterstrich?

Wie kann ich kurzfristige Informationsübertragung zwischen Seiten im BeEGO-Framework implementieren? Wie kann ich kurzfristige Informationsübertragung zwischen Seiten im BeEGO-Framework implementieren? Mar 03, 2025 pm 05:22 PM

Wie kann ich kurzfristige Informationsübertragung zwischen Seiten im BeEGO-Framework implementieren?

Wie konvertieren Sie die Liste der MySQL -Abfrageergebnisse in eine benutzerdefinierte Struktur -Slice in Go -Sprache? Wie konvertieren Sie die Liste der MySQL -Abfrageergebnisse in eine benutzerdefinierte Struktur -Slice in Go -Sprache? Mar 03, 2025 pm 05:18 PM

Wie konvertieren Sie die Liste der MySQL -Abfrageergebnisse in eine benutzerdefinierte Struktur -Slice in Go -Sprache?

Wie kann ich benutzerdefinierte Typ -Einschränkungen für Generika in Go definieren? Wie kann ich benutzerdefinierte Typ -Einschränkungen für Generika in Go definieren? Mar 10, 2025 pm 03:20 PM

Wie kann ich benutzerdefinierte Typ -Einschränkungen für Generika in Go definieren?

Wie schreibe ich Scheinobjekte und Stubs zum Testen in Go? Wie schreibe ich Scheinobjekte und Stubs zum Testen in Go? Mar 10, 2025 pm 05:38 PM

Wie schreibe ich Scheinobjekte und Stubs zum Testen in Go?

Wie schreibe ich Dateien in Go Language bequem? Wie schreibe ich Dateien in Go Language bequem? Mar 03, 2025 pm 05:15 PM

Wie schreibe ich Dateien in Go Language bequem?

Wie schreibt man Unit -Tests in Go? Wie schreibt man Unit -Tests in Go? Mar 21, 2025 pm 06:34 PM

Wie schreibt man Unit -Tests in Go?

Wie kann ich Tracing -Tools verwenden, um den Ausführungsfluss meiner GO -Anwendungen zu verstehen? Wie kann ich Tracing -Tools verwenden, um den Ausführungsfluss meiner GO -Anwendungen zu verstehen? Mar 10, 2025 pm 05:36 PM

Wie kann ich Tracing -Tools verwenden, um den Ausführungsfluss meiner GO -Anwendungen zu verstehen?

See all articles