Goでデータベースにアクセスする#
データベースとは、アプリケーションの情報を保存・取得する場所です。
GoではGORM、sqlx、標準のdatabase/sql
パッケージなど、様々なライブラリを使ってデータベースにアクセスできます。
一般的にモデルとは、システム上の登場人物(登場するモノ)を指します。
Goでも、モデルは登場人物単位で作ると良いです。 例えば予約システムだと、モデルには「お客様」と、「予約」という登場人物があり、 「お客様」には、氏名、電話番号、アカウント名があり、 「予約」には、日時、注文内容がある、 ような感じです。
ここでは、「お客様」情報としてUserモデルを作ってみます。SQLiteデータベースを使い、GORMというORMライブラリを使用します。
前提#
もし、Goプロジェクトが手元になければ、こちらの手順でGoプロジェクトを用意してください。
GORMのセットアップ#
まず、GORMとSQLiteドライバをインストールします:
go get -u gorm.io/gorm
go get -u gorm.io/driver/sqlite
プロジェクト構造の設定#
より整理された構造でプロジェクトを進めるため、以下のようなディレクトリ構造を作成しましょう:
go-web-server/
├── main.go
├── models/
│ └── user.go
├── database/
│ └── database.go
└── handlers/
└── user_handler.go
必要なディレクトリを作成します:
mkdir -p models database handlers
モデルの定義#
models/user.go
ファイルを作成して、ユーザーモデルを定義します:
package models
import (
"time"
"gorm.io/gorm"
)
// User はユーザー情報を表す構造体です
type User struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" gorm:"not null"`
Tel string `json:"tel" gorm:"not null"`
AccountName string `json:"account_name" gorm:"not null;unique"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
}
// NewUser はユーザー作成時に使用する構造体です
type NewUser struct {
Name string `json:"name" binding:"required"`
Tel string `json:"tel" binding:"required"`
AccountName string `json:"account_name" binding:"required"`
}
データベース接続の設定#
database/database.go
ファイルを作成して、データベース接続を管理します:
package database
import (
"log"
"go-web-server/models"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// DB はアプリケーション全体で使用するデータベース接続です
var DB *gorm.DB
// InitDatabase はデータベースを初期化します
func InitDatabase() {
var err error
// SQLiteデータベースに接続
DB, err = gorm.Open(sqlite.Open("app.db"), &gorm.Config{})
if err != nil {
log.Fatal("データベースへの接続に失敗しました:", err)
}
// マイグレーションを実行(テーブルを自動作成)
err = DB.AutoMigrate(&models.User{})
if err != nil {
log.Fatal("マイグレーションに失敗しました:", err)
}
log.Println("データベースの初期化が完了しました")
}
ユーザー操作のハンドラー#
handlers/user_handler.go
ファイルを作成して、ユーザーのCRUD処理を実装します:
package handlers
import (
"net/http"
"strconv"
"go-web-server/database"
"go-web-server/models"
"github.com/gin-gonic/gin"
)
// GetAllUsers は全ユーザーを取得するハンドラーです
func GetAllUsers(c *gin.Context) {
var users []models.User
result := database.DB.Find(&users)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "ユーザーの取得に失敗しました"})
return
}
c.JSON(http.StatusOK, users)
}
// GetUser は特定のユーザーを取得するハンドラーです
func GetUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "無効なIDです"})
return
}
var user models.User
result := database.DB.First(&user, id)
if result.Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "ユーザーが見つかりません"})
return
}
c.JSON(http.StatusOK, user)
}
// CreateUser は新しいユーザーを作成するハンドラーです
func CreateUser(c *gin.Context) {
var input models.NewUser
// クエリパラメータからデータを取得
input.Name = c.Query("name")
input.Tel = c.Query("tel")
input.AccountName = c.Query("account_name")
// 必須フィールドの検証
if input.Name == "" || input.Tel == "" || input.AccountName == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "name, tel, account_name は必須です"})
return
}
// 新しいユーザーを作成
user := models.User{
Name: input.Name,
Tel: input.Tel,
AccountName: input.AccountName,
}
result := database.DB.Create(&user)
if result.Error != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "ユーザーの作成に失敗しました"})
return
}
c.JSON(http.StatusCreated, user)
}
// UpdateUser はユーザー情報を更新するハンドラーです
func UpdateUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "無効なIDです"})
return
}
// 既存のユーザーを取得
var user models.User
result := database.DB.First(&user, id)
if result.Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "ユーザーが見つかりません"})
return
}
// クエリパラメータから更新データを取得
name := c.Query("name")
tel := c.Query("tel")
accountName := c.Query("account_name")
// 提供されたフィールドのみ更新
if name != "" {
user.Name = name
}
if tel != "" {
user.Tel = tel
}
if accountName != "" {
user.AccountName = accountName
}
// データベースを更新
database.DB.Save(&user)
c.JSON(http.StatusOK, user)
}
// DeleteUser はユーザーを削除するハンドラーです
func DeleteUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "無効なIDです"})
return
}
result := database.DB.Delete(&models.User{}, id)
if result.Error != nil || result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "ユーザーが見つかりません"})
return
}
c.JSON(http.StatusNoContent, nil)
}
メインアプリケーションの更新#
最後に、main.go
ファイルを更新して、すべてを統合します:
package main
import (
"fmt"
"log"
"go-web-server/database"
"go-web-server/handlers"
"github.com/gin-gonic/gin"
)
func main() {
// データベースを初期化
database.InitDatabase()
// Ginルーターを初期化
r := gin.Default()
// APIエンドポイントを設定
r.GET("/api/users", handlers.GetAllUsers)
r.GET("/api/users/:id", handlers.GetUser)
r.GET("/api/users/create", handlers.CreateUser)
r.GET("/api/users/:id/update", handlers.UpdateUser)
r.GET("/api/users/:id/delete", handlers.DeleteUser)
// 基本的なヘルスチェックエンドポイント
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{"text": "こんにちは!"})
})
// サーバーを起動
fmt.Println("サーバーを起動します: http://localhost:8080")
log.Fatal(r.Run(":8080"))
}
モジュールパスの修正#
モジュールパスをプロジェクトに合わせるため、go.mod
ファイルを編集します。例えば:
module go-web-server
go 1.20
require (
github.com/gin-gonic/gin v1.9.1
gorm.io/driver/sqlite v1.5.2
gorm.io/gorm v1.25.2
// その他の依存関係...
)
APIの使い方#
サーバーを起動します:
go run main.go
以下のURLでAPIを使用できます:
- ユーザー作成:
http://localhost:8080/api/users/create?name=比古清十郎&tel=000(0000)0000&account_name=hiko
- ユーザー一覧取得:
http://localhost:8080/api/users
- 特定ユーザー取得:
http://localhost:8080/api/users/1
(IDは作成されたユーザーのIDに置き換えてください) - ユーザー更新:
http://localhost:8080/api/users/1/update?name=弥彦&tel=111(1111)1111
(IDは更新したいユーザーのIDに置き換えてください) - ユーザー削除:
http://localhost:8080/api/users/1/delete
(IDは削除したいユーザーのIDに置き換えてください)
GORM の便利な機能#
GORM にはたくさんの便利な機能があります:
- 自動マイグレーション:
AutoMigrate
を使用してテーブルスキーマを自動作成・更新できます。 - 複雑なクエリ:
Where
,Or
,Not
,Order
などのメソッドを使って複雑なクエリを実行できます。 - 関連付け:
has one
,has many
,belongs to
,many to many
などの関連付けをサポートしています。 - フック:
BeforeSave
,AfterCreate
などのフックを使用して、モデルのライフサイクルイベントに処理を追加できます。 - トランザクション:
Transaction
メソッドを使用して、複数の操作をアトミックに実行できます。
例えば、より複雑なクエリは以下のように記述できます:
// 特定の条件に一致するユーザーを検索
var users []models.User
database.DB.Where("name LIKE ?", "%清十郎%").Find(&users)
// 順序付けと制限付きの検索
database.DB.Order("created_at desc").Limit(10).Find(&users)
// 集計関数の使用
var count int64
database.DB.Model(&models.User{}).Count(&count)
これでGoでデータベースを扱うことができました。 次はReactという仕組みを使って画面を作っていきましょう!