Implémentez un CRUD simple avec Go + MySQL + Docker

introduction

L'autre jour, j'ai pensé: "Je n'ai pas l'intention de l'utiliser pour les affaires, mais je devrais connaître Go dans une certaine mesure ...", alors j'ai essayé d'implémenter un simple CRUD en utilisant Go, donc je vais résumer la méthode sous forme de mémorandum. Je vais.

En gros, c'est un peu arrangé en combinant les contenus des sites suivants. Veuillez vous référer à ces sites car ils ont été très utiles dans l'étude de Go. Création d'un environnement de développement Go avec Docker Créez une application Web très simple avec Go / Gin Introduction à Go Language-MySQL Connection- Créer un conteneur de base de données pour docker-compose MySQL8.0 Comment attendre que MySQL démarre avec docker-compose up (2 types introduits)

Lancez Go avec Docker

Tout d'abord, nous allons lancer Go with Docker. Directement sous le répertoire de travail

DockerFile


FROM golang:latest

RUN mkdir /app
WORKDIR /app

docker-compose.yml


version: '3'
services:
  go:
    build:
      context: .
      dockerfile: DockerFile
    command: /bin/sh -c "go run main.go"
    stdin_open: true
    tty: true
    volumes:
      - .:/app

main.go


package main

import "fmt"

func main() {
  fmt.Println("Hello, World!")
}

Si vous faites docker-compose up avec ceci, vous verrez Hello, World! Sur la console. Si cela se produit, le lancement de Go est réussi.

Je vais expliquer brièvement chaque fichier. ・ DockerFile Créez un conteneur Go (environnement virtuel). En spécifiant WORKDIR / app ici, toutes les opérations suivantes seront effectuées sous / app.

・ Docker-compose.yml Ecrivez les paramètres lors du lancement du conteneur créé avec DockerFile. Cela lancera un conteneur dans DockerFile et lancera la commande go run main.go pour lancer main.go.

・ Main.go J'écrirai le traitement lié à Go ici. Cette fois, tout ce que vous avez à faire est de sortir Hello, World!.

Créer une page Web dans Go

Maintenant que Go a commencé, créons une page Web à l'aide de Go. Cette fois, j'utiliserai un framework appelé Gin. --Ajouter l'installation à DockerFile

DockerFile


FROM golang:latest

RUN mkdir /app
WORKDIR /app

RUN go get github.com/gin-gonic/gin

docker-compose.yml


version: '3'
services:
  go:
    build:
      context: .
      dockerfile: DockerFile
    command: /bin/sh -c "go run main.go"
    stdin_open: true
    tty: true
    volumes:
      - .:/app
    ports:
      - 8080:8080

main.go


package main

import (
  "github.com/gin-gonic/gin"
)

func main() {
  router := gin.Default()
  router.LoadHTMLGlob("templates/*.html")

  router.GET("/", func(ctx *gin.Context){
    ctx.HTML(200, "index.html", gin.H{})
  })

  router.Run()
}

templates/index.html


<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sample App</title>
</head>
<body>
  <h1>Hello World!!!</h1>
</body>
</html>

avec ça

docker-compose build
docker-compose up -d

Après être allé et attendu un moment Si vous accédez à [http: // localhost: 8080](http: // localhost: 8080), vous verrez Hello World!.

Je vais vous expliquer ce que j'ai fait cette fois. Cette fois, j'ai ajouté un framework appelé Gin. Gin est installé avec la commande go get github.com / gin-gonic / gin après avoir créé un conteneur avec DockerFile et est appelé dans main.go. Et le contenu des modèles est lu dans main.go,

router.GET("/", func(ctx *gin.Context){
  ctx.HTML(200, "index.html", gin.H{})
})

Sera associé à templates / index.html pour root ("/"). En passant, si vous changez le premier argument ("/") de router.GET en "/ test" etc., ce ne sera pas [http: // localhost: 8080](http: // localhost: 8080) mais [http: // localhost Index.html sera affiché à l'adresse: 8080 / test](http: // localhost: 8080 / test).

Enfin, ajoutez un port à docker-compose.yml pour autoriser l'accès à localhost: 8080.

Démarrez MySQL avec Docker

À ce stade, vous pouvez créer des pages Web avec Go. Cependant, en réalité, lors de la création d'un service Web, la connexion avec la base de données est inévitable. Ensuite, nous allons lancer MySQL en utilisant Docker.

Premier dans docker-compose.yml -Description du conteneur db ・ Description du volume S'il-vous-plait ajoutez.

docker-compose.yml


db:
  image: mysql:8.0
  environment:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_DATABASE: go_database
    MYSQL_USER: go_test
    MYSQL_PASSWORD: password
    TZ: 'Asia/Tokyo'
  command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  volumes:
    - db-data:/var/lib/mysql
    - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
  ports:
    - 3306:3306

volumes:
  db-data:
    driver: local

Créez également un répertoire db et créez-y un fichier my.cnf.

my.cnf


[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_bin

default-time-zone = SYSTEM
log_timestamps = SYSTEM

default-authentication-plugin = mysql_native_password

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

(Cette zone est la même que la page de référence. Seule la partie liée au journal n'a pas fonctionné pour une raison quelconque, je l'ai donc supprimée)

Si vous faites docker-compose up -d jusqu'à présent, le conteneur MySQL devrait également démarrer. Puisqu'il n'y a qu'une description des paramètres, l'explication ici est omise.

Connectez Go et MySQL

Maintenant que MySQL a démarré, je vais le connecter à Go immédiatement. Cette fois, nous utiliserons le pilote sql et un framework appelé GORM pour la connexion. --Ajouter l'installation à DockerFile

DockerFile


FROM golang:latest

RUN mkdir /app
WORKDIR /app

RUN go get github.com/gin-gonic/gin
RUN go get github.com/go-sql-driver/mysql
RUN go get github.com/jinzhu/gorm

docker-compose.yml


version: '3'
services:
  go:
    build:
      context: .
      dockerfile: DockerFile
    command: /bin/sh -c "go run main.go"
    stdin_open: true
    tty: true
    volumes:
      - .:/app
    ports:
      - 8080:8080
    depends_on:
      - "db"

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: go_database
      MYSQL_USER: go_test
      MYSQL_PASSWORD: password
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - db-data:/var/lib/mysql
      - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
    ports:
      - 3306:3306

volumes:
  db-data:
    driver: local

main.go


package main

import (
  "fmt"
  "time"

  "github.com/gin-gonic/gin"
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

func main() {
  db := sqlConnect()
  defer db.Close()
	
  router := gin.Default()
  router.LoadHTMLGlob("templates/*.html")

  router.GET("/", func(ctx *gin.Context){
    ctx.HTML(200, "index.html", gin.H{})
  })

  router.Run()
}

func sqlConnect() (database *gorm.DB) {
  DBMS := "mysql"
  USER := "go_test"
  PASS := "password"
  PROTOCOL := "tcp(db:3306)"
  DBNAME := "go_database"

  CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
  
  count := 0
  db, err := gorm.Open(DBMS, CONNECT)
  if err != nil {
    for {
      if err == nil {
        fmt.Println("")
        break
      }
      fmt.Print(".")
      time.Sleep(time.Second)
      count++
      if count > 180 {
        fmt.Println("")
        fmt.Println("Échec de la connexion à la base de données")
        panic(err)
      }
      db, err = gorm.Open(DBMS, CONNECT)
    }
  }
  fmt.Println("Connexion à la base de données réussie")

  return db
}

Maintenant, faites docker compose up et si la console dit" Connexion DB réussie ", c'est réussi.

Puisque sqlConnect est le contenu principal ajouté, je vais l'expliquer.

func sqlConnect() (database *gorm.DB) {
  DBMS := "mysql"
  USER := "go_test"
  PASS := "password"
  PROTOCOL := "tcp(db:3306)"
  DBNAME := "go_database"

  CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
  
  count := 0
  db, err := gorm.Open(DBMS, CONNECT)
  if err != nil {
    for {
      if err == nil {
        fmt.Println("")
        break
      }
      fmt.Print(".")
      time.Sleep(time.Second)
      count++
      if count > 180 {
        fmt.Println("")
        fmt.Println("Échec de la connexion à la base de données")
        panic(err)
      }
      db, err = gorm.Open(DBMS, CONNECT)
    }
  }
  fmt.Println("Connexion à la base de données réussie")

  return db
}

La première moitié définit les informations de connexion à la base de données. Veuillez entrer le contenu défini dans docker-compose.yml. Puis connectez-vous à la base de données avec db, err: = gorm.Open (SGBD, CONNECT). Cependant, en fonction de l'heure de démarrage de MySQL, MySQL peut ne pas être prêt au moment où cette commande est exécutée. Par conséquent, ce code prend deux mesures.

Le premier est le paramètre de dépendance dans docker-compose.yml. En définissant depend_on ici, le conteneur go sera démarré après le démarrage du conteneur db.

Le second est une nouvelle tentative de traitement. Même après le démarrage du conteneur db, MySQL met du temps à démarrer, donc s'il ne se connecte pas à la base de données, j'attends 1 seconde puis réessaye. Si tel est le cas, il continuera à réessayer en cas d'erreur, renvoyez donc l'erreur un nombre approprié de fois. Dans ce code, s'il ne se connecte pas pendant 3 minutes, une erreur se produira.

Mettre en œuvre CRUD

Enfin, il est connecté à MySQL, alors implémentons enfin le traitement CRUD et voyons le flux réel. Seuls main.go et index.html seront modifiés. --Créer la définition de l'utilisateur --Migration --Mise en place du post-traitement

main.go


package main

import (
  "fmt"
  "strconv"
  "time"

  "github.com/gin-gonic/gin"
  "github.com/jinzhu/gorm"
  _ "github.com/go-sql-driver/mysql"
)

type User struct {
  gorm.Model
  Name string
  Email string
}

func main() {
  db := sqlConnect()
  db.AutoMigrate(&User{})
  defer db.Close()

  router := gin.Default()
  router.LoadHTMLGlob("templates/*.html")

  router.GET("/", func(ctx *gin.Context){
    db := sqlConnect()
    var users []User
    db.Order("created_at asc").Find(&users)
    defer db.Close()

    ctx.HTML(200, "index.html", gin.H{
      "users": users,
    })
  })

  router.POST("/new", func(ctx *gin.Context) {
    db := sqlConnect()
    name := ctx.PostForm("name")
    email := ctx.PostForm("email")
    fmt.Println("create user " + name + " with email " + email)
    db.Create(&User{Name: name, Email: email})
    defer db.Close()

    ctx.Redirect(302, "/")
  })

  router.POST("/delete/:id", func(ctx *gin.Context) {
    db := sqlConnect()
    n := ctx.Param("id")
    id, err := strconv.Atoi(n)
    if err != nil {
      panic("id is not a number")
    }
    var user User
    db.First(&user, id)
    db.Delete(&user)
    defer db.Close()

    ctx.Redirect(302, "/")
  })

  router.Run()
}

func sqlConnect() (database *gorm.DB) {
  DBMS := "mysql"
  USER := "go_test"
  PASS := "password"
  PROTOCOL := "tcp(db:3306)"
  DBNAME := "go_database"

  CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
  
  count := 0
  db, err := gorm.Open(DBMS, CONNECT)
  if err != nil {
    for {
      if err == nil {
        fmt.Println("")
        break
      }
      fmt.Print(".")
      time.Sleep(time.Second)
      count++
      if count > 180 {
        fmt.Println("")
        panic(err)
      }
      db, err = gorm.Open(DBMS, CONNECT)
    }
  }

  return db
}

templates/index.html


<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Sample App</title>
</head>
<body>
  <h2>Ajouter un utilisateur</h2>
  <form method="post" action="/new">
    <p>Nom<input type="text" name="name" size="30" placeholder="Entrez s'il vous plait" ></p>
    <p>adresse mail<input type="text" name="email" size="30" placeholder="Entrez s'il vous plait" ></p>
    <p><input type="submit" value="Send"></p>
  </form>
  
  <h2>Liste d'utilisateur</h2>
  <table>
    <tr>
      <td>Nom</td>
      <td>adresse mail</td>
    </tr>
    {{ range .users }}
      <tr>
        <td>{{ .Name }}</td>
        <td>{{ .Email }}</td>
        <td>
          <form method="post" action="/delete/{{.ID}}">
            <button type="submit">Supprimer</button>
          </form>
        </td>
      </tr>
    {{ end }}
  </ul>
  </body>
</html>

Maintenant, faites docker-compose up -d, et lorsque vous accédez à [http: // localhost: 8080](http: // localhost: 8080), le formulaire d'inscription de l'utilisateur apparaîtra, et lorsque vous enregistrez l'utilisateur, l'utilisateur enregistré ci-dessous Les informations seront affichées. De plus, même si vous supprimez le conteneur et le relancez, l'utilisateur enregistré ne sera pas supprimé et restera affiché dans la liste des utilisateurs.

Ensuite, je vais expliquer la partie supplémentaire. Tout d'abord, je crée une structure appelée User dans main.go. Dans gorm.Model, le contenu nécessaire pour le modèle, tel que l'identifiant, est placé dans Utilisateur, et le nom et l'adresse e-mail spécifiques à l'utilisateur sont ajoutés. Cette structure est reflétée dans la base de données par db.AutoMigrate.

Ensuite, nous implémenterons le traitement CRUD dans chaque chemin.

Obtenez la liste des utilisateurs dans le chemin racine. Utilisez db.Find (& users) pour obtenir la liste des utilisateurs dans la base de données en tant que structure User. En plaçant une commande entre les deux, l'ancien utilisateur apparaît au moment de l'acquisition. Nous transmettons le dernier utilisateur récupéré à index.html.

Le chemin / new crée un utilisateur basé sur le contenu du formulaire. J'obtiens le contenu soumis par le formulaire avec ctx.PostForm et je persiste le contenu avec db.Create. Redirigez vers root lorsque vous avez terminé.

Le chemin / delete spécifie l'id pour supprimer l'utilisateur. Ici, l'identifiant de l'utilisateur est spécifié dans l'URL, mais il est également obtenu à partir de ctx. Ensuite, récupérez l'utilisateur à partir du contenu avec db.First et supprimez l'utilisateur avec db.Delete. Notez que l'id est passé sous forme de chaîne, donc strconv.Atoi le convertit en type int.

Dans index.html, le formulaire et la table sont créés par la méthode d'écriture html générale. Ici, nous recevons les utilisateurs passés de main.go sous la forme {{range .users}}.

en conclusion

Cette fois, j'ai essayé d'implémenter un CRUD simple avec Go + MySQL + Docker comme introduction au développement de services Web avec Go. C'est juste une pratique, donc je ne pense pas à la validation ou au contrôle fin. Le contenu que j'ai créé cette fois est rudimentaire, mais je pense que vous pouvez réellement créer un service Web en développant et en compliquant ce contenu. Si quelqu'un veut faire quelque chose avec Go, veuillez vous y référer!

Recommended Posts

Implémentez un CRUD simple avec Go + MySQL + Docker
Implémenter CRUD avec Spring Boot + Thymeleaf + MySQL
[Construction de l'environnement avec Docker] Rails 6 et MySQL 8
Mettre à jour MySQL de 5.7 à 8.0 avec Docker
Implémentez un serveur API Web REST simple avec Spring Boot + MySQL
Construire un environnement Rails 6 + MySQL avec Docker compose
Déployer sur heroku avec Docker (Rails 6, MySQL)
Modifier Mysql avec des commandes dans l'environnement Docker
[docker] [nginx] Créer un ALB simple avec nginx
Construire un environnement pour Laravel + MySQL + phpMyadmin avec Docker
Autoriser la publication d'images avec [Docker + WordPress + MySQL]
Créez un tableau d'affichage simple avec Java + MySQL
Entraînez-vous à créer une application de chat simple avec Docker + Sinatra
Implémentez une API Rest simple avec Spring Security avec Spring Boot 2.0
Environnement Build Rails (API) x MySQL x Nuxt.js avec Docker
Exploser Docker avec WSL2
Utiliser Puphpeteer avec Docker
Exploitez Emby avec Docker
Exécutez Payara avec Docker
[Rails] Développement avec MySQL
J'ai essayé de créer un serveur API avec Go (Echo) x MySQL x Docker x Clean Architecture
PHP jetable avec Docker
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ③ ~ Ajouter une validation ~
Comment créer un environnement [TypeScript + Vue + Express + MySQL] avec Docker ~ Express ~
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ① ~ Hello World ~
Créez un environnement de développement d'applications Rails avec Docker [Docker, Rails, Puma, Nginx, MySQL]
Créez un CRUD simple avec SpringBoot + JPA + Thymeleaf ⑤ ~ Modèle commun ~
docker-compose.yml lorsque vous voulez garder mysql en cours d'exécution avec docker
Un exemple CRUD simple utilisant Java Servlet / JSP et MySQL
Implémentez une API Rest simple avec Spring Security & JWT avec Spring Boot 2.0
Mesures des autorisations lors de la construction de MySQL avec Docker sur WSL2