Maison > développement back-end > Golang > le corps du texte

Comment utiliser les migrations avec Golang

Linda Hamilton
Libérer: 2024-11-09 07:28:02
original
868 Les gens l'ont consulté

Exemple d'application simple montrant comment utiliser golang-migrate

Pourquoi devriez-vous utiliser les migrations ?

Beaucoup de gens posent cette question et j'ai essayé de faire cette liste pour mettre en évidence les principaux avantages de l'utilisation des migrations :

Contrôle de version : L'un des principaux et des plus importants est de pouvoir avoir un versionnage des différentes modifications du schéma de la base de données. Sans les migrations, ces modifications de schéma seraient incohérentes et impossibles à suivre, ce qui entraînerait des problèmes de version et d'éventuelles erreurs.

Rollback : Il est toujours nécessaire d'avoir un système de rollback en cas de panne. Un système de migrations dispose toujours de deux méthodes :-)

Automation et intégration CI/CD : Les migrations peuvent être automatisées, leur permettant de faire partie du pipeline CI/CD. Cela aide à déployer les changements de manière fluide et cohérente sans intervention manuelle.

Nous pouvons trouver bien d'autres avantages mais je pense que ces points représentent un bon résumé des principaux avantages.

Comment mettre en œuvre les migrations dans Golang ?

Go ne prend pas en charge les migrations de manière native pour cette raison, nous pouvons également utiliser le package populaire golang-migrate si vous utilisez un ORM comme GORM, vous pouvez l'utiliser pour cela.

Les deux packages sont très populaires mais dans cet exemple, j'utiliserai golang-migrate car je ne suis pas intéressé par l'implémentation d'un ORM.

Montre-moi le code !

Voyons étape par étape comment implémenter une application simple pour voir comment elle est utilisée.

Pour suivre cet article vous aurez besoin de : Go et Docker avec Docker Compose

Infrastructure

Créez le fichier docker-compose.yml dans votre répertoire racine où nous définirons votre base de données préférée, dans mon cas utilisez une MariaDB mais n'hésitez pas à en utiliser une autre.

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 
Copier après la connexion
Copier après la connexion
Copier après la connexion

Si vous préférez, vous pouvez utiliser Docker directement au lieu de docker-compose :

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
Copier après la connexion
Copier après la connexion
Copier après la connexion

Valeurs environnementales

Créez ou mettez à jour le fichier .env dans votre répertoire racine où vous devez définir les variables pour connecter notre base de données.

DATABASE_DSN=root:root@tcp(localhost:3306)/app
Copier après la connexion
Copier après la connexion
Copier après la connexion

Créez une application Golang simple

Créez une application Golang simple pour garantir une connexion réussie à la base de données et répertoriez toutes les tables et structures de la base de données avec leur structure. 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()
 }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Et lorsque nous l'exécutons, nous obtenons un résultat similaire :

How to use migrations with Golang

Migrer la CLI

Pour exécuter golang-migrate CLI, vous disposez essentiellement de deux méthodes : installer la CLI localement ou exécuter via l'image Docker officielle : migrate/migrate.

Personnellement, je préfère la variante Docker mais dans ce tutoriel, illustrez les deux variantes.

Comment générer une migration

La première étape consiste à créer une migration vide avec la commande suivante.

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 
Copier après la connexion
Copier après la connexion
Copier après la connexion
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
Copier après la connexion
Copier après la connexion
Copier après la connexion
  • ext : Extension du fichier à générer.
  • dir : Répertoire où notre migration sera créée.
  • seq : Nom de la séquence de migration.

Cette commande générera deux fichiers vides sur le dossier database/migrations/ : 000001createuserstable.up.sql et 000001createuserstable.down.sql

Dans le fichier 000001createuserstable.up.sql, définissez SQL pour créer une table d'utilisateurs :

DATABASE_DSN=root:root@tcp(localhost:3306)/app
Copier après la connexion
Copier après la connexion
Copier après la connexion

Dans le fichier 000001createuserstable.down.sql, définissez SQL pour annuler toutes les modifications apportées par up, dans ce cas nous devons supprimer la table des utilisateurs :

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()
 }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Comment appliquer la migration

La commande suivante applique toutes les migrations en attente. Vous pouvez également définir le nombre de migrations à appliquer en additionnant le nombre après le haut.

#CLI variant
migrate create -ext sql -dir ./database/migrations -seq create_users_table
Copier après la connexion
Copier après la connexion
#Docker CLI variant
docker run --rm -v $(pwd)/database/migrations:/migrations migrate/migrate \
    create -ext sql -dir /migrations -seq create_users_table
Copier après la connexion
  • path : chemin d’accès au répertoire des migrations.
  • base de données : définissez votre connexion DSN à la base de données.

REMARQUE : Lors de la première exécution de la migration une table "schema_migrations" sera créée dans laquelle la migration connaît le numéro de version appliqué.

Et lancez notre application Golang pour afficher les résultats :

How to use migrations with Golang

Ajout d'une nouvelle migration

Ajouter une nouvelle colonne de téléphone sur la table des utilisateurs

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
);
Copier après la connexion
DROP TABLE IF EXISTS `users`;
Copier après la connexion
#CLI variant
migrate -path=./database/migrations -database "mysql://root:root@tcp(localhost:3306)/app" up
Copier après la connexion
#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
Copier après la connexion

Et lorsque vous l'exécutez depuis notre application Golang, vous pouvez voir le nouveau champ :

How to use migrations with Golang

Comment annuler la migration

Avec la commande suivante, nous pouvons facilement annuler le fichier appliqué. migrations. Dans l'exemple suivant, nous pouvons voir comment nous annulons la dernière migration appliquée :

#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
Copier après la connexion
-- 000002_add_column_phone.up.sql
ALTER TABLE `users` ADD `phone` VARCHAR(255) NULL;
Copier après la connexion

ATTENTION : Si vous ne définissez pas le nombre de migrations, le ROLLBACK sera appliqué à TOUTES LES MIGRATIONS !

Et puis nous pouvons montrer que la dernière migration a été annulée et que le champ téléphone a été supprimé :-)

Display table users without phone field

Comment résoudre les erreurs de migration

Si une migration contient des erreurs et est exécutée, cette migration ne peut pas être appliquée et le système de migration empêchera toute autre migration sur la base de données jusqu'à ce que cette migration soit corrigée.

Et lorsque nous essaierons de postuler, nous recevrons un message comme celui-ci :

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 
Copier après la connexion
Copier après la connexion
Copier après la connexion

Pas de panique, il n'est pas difficile de revenir à un système cohérent.

Nous devons d'abord résoudre la migration corrompue, dans ce cas la version 2.

Une fois la migration résolue, nous devons forcer le système à la dernière version valide, en l'occurrence la 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
Copier après la connexion
Copier après la connexion
Copier après la connexion
DATABASE_DSN=root:root@tcp(localhost:3306)/app
Copier après la connexion
Copier après la connexion
Copier après la connexion

Et maintenant vous pouvez réappliquer les migrations sans aucun problème ;-)

Makefile

Pour améliorer notre productivité et faciliter l'utilisation de ces commandes nous pouvons utiliser Makefile. Ci-dessous vous pouvez voir les deux variantes : client natif et docker.

Variante CLI

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()
 }
}
Copier après la connexion
Copier après la connexion
Copier après la connexion

Variante Docker CLI

#CLI variant
migrate create -ext sql -dir ./database/migrations -seq create_users_table
Copier après la connexion
Copier après la connexion

Dépôt

Le code de ce tutoriel peut être trouvé dans le public : GitHub - albertcolom/example-go-migration


Original publié sur : albertcolom.com

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Derniers articles par auteur
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal