Golang 入門 : Web(簡易掲示板 ) を作成 part2-テーブル定義とモデル定義

この記事は約10分で読めます。

前回は Golang

Golang 入門 : Webサイト(簡易掲示板 ) を作成 part1-プロジェクト作成
Golang で Webアプリ( 掲示板 )を作成してみます。とても勉強になるので、DB(MySQL)への接続も取り扱うので、一通りWebサービス作成を体験できると思います。また、web初めての方でもGoで簡単に作れるようにしているので、是非作成してみてください。

でした。

今回は,データベースのテーブル定義を行い、そのデータベースのデータを扱うために、モデルを作成します。

ディレクトリ構成

今回は「ここ」を扱います。

.
├── controller
│   └── article_handler.go
├── go.mod //go mod initで作成
├── go.sum //自動生成
├── model
│   └── article.go <--- ここ
├── script
│   ├── reset_db.sh <---ここ
│   └── reset_db.sql <- ここ
├── util
│   └── db.go <---------ここ
├── view
│   ├── index.tmpl
│   ├── layout.tmpl
│   └── new.tmpl
└── web.go

MySQLのテーブル定義

reset_db.sh

mysql -uroot < ./script/reset_db.sql

reset_db.sql

DROP DATABASE IF EXISTS web_go;
CREATE DATABASE web_go;

use web_go;

CREATE TABLE `articles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
  `text` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

説明

DROP DATABASE IF EXISTS web_go;

CREATE DATABASE web_go;

ではweb_goというデータベースを作成しました。

IF EXISTSを用いることで、すでにデータベースが存在している場合、一度データベースを削除し、データベースを再度作成しています。

また、テーブルの構造は

  • id…プライマリキー、データを一意に決める数字
  • created_at…データがinsertされた時の時間(gormによって、自動挿入される)
  • updated_at…データがupdateされた時の時間(gormによって、自動挿入されるが今回は特に利用しない)
  • deleted_at…データが論理削除された時の時間(gormによって削除時に自動挿入される)
  • text…固有のカラム(記事の内容が入る)

上記のようになっています。

 

reset_db.shを作成しておくことで

$ sh script/reset_db.sh

とすることで、データを初期化することができます

余談ですが、投稿にユーザのデータを入れたい時は、ここにUserIDカラムを追加し、ArticleテーブルとUserテーブルの関係を持たせたりします。

MySQLに接続する処理

util/db.goは、上記で設定したデータベースにアプリケーションからアクセスする初期化処理を行います。

今回、データベースとアプリのつなぎ目をGormというパッケージを利用しています。

 

db.go

package util

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

type DBConfig struct {
	User     string
	Password string
	Host     string
	Port     string
	Database string
	Debug    bool
}

func InitDB() *gorm.DB {
	conf := DBConfig{
		User:     "root",
		Password: "",
		Host:     "localhost",
		Port:     "3306",
		Database: "web_go",
		Debug:    true,
	}
	param := conf.User + ":" + conf.Password + "@tcp(" + conf.Host + ":" + conf.Port + ")/" +
		conf.Database + "?parseTime=true&loc=UTC&charset=utf8mb4"
	db, err := gorm.Open("mysql", param)
	if err != nil {
		panic(err)
	}
	if conf.Debug {
		db = db.Debug()
	}
	return db
}

 説明

重要なのが、

_ “github.com/jinzhu/gorm/dialects/mysql”

です。この処理を記述していないと上手く接続できません。忘れないようにしてください。

DBConfigを作成し、それを元にgorm.DBオブジェクトを作成しています。各々、自分のMySQLにあったconfig設定にしてください。(password設定している人は、passwordに文字列を入れます)

Configにまとめたのは、後々、yamlやtomlファイルからDB接続情報を呼び出すときに、データの取り出しが簡単になるからです。

 

Model

ついにアプリケーション側の処理に移ります。

アプリを作成する時は、modelから書き始めるのがお勧めです。基本的にここは他の層に比べて変更が少ないところです。(依存が少ないため)

 

article.go

package model

import (
	"github.com/jinzhu/gorm"
)

type Article struct {
	gorm.Model
	Text string
}

func GetAllArticles(db *gorm.DB) ([]Article, error) {
	articles := make([]Article, 10)
	if err := db.Find(&articles).Error; err != nil {
		return nil, err
	}
	return articles, nil
}

func GetArticleByID(db *gorm.DB, id uint) (*Article, error) {
	article := new(Article)
	if err := db.Where("id = ?", id).First(&article).Error; err != nil {
		return nil, err
	}
	return article, nil
}

func PostArticle(db *gorm.DB, article *Article) error {
	return db.Create(&article).Error
}

func DeleteArticleByID(db *gorm.DB, id uint) error {
	return db.Where("id = ?", id).Delete(Article{}).Error
}

 解説

Article構造体

type Article struct { gorm.Model Text string }

ここが重要ですね。Articleストラクトは、アプリで扱う構造体で、データベースから取得したデータを格納します。
gorm.Modelには、テーブル定義にもあった以下の情報が入っています。

`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
気をつけてほしいところは、構造体のなかの変数(今回はText)は必ず大文字から書き始めることです。
Golang では大文字がpublic、小文字がprivateの意味をなします。
そのため、大文字出ないと変数にアクセスできなくなります。

Article処理

ここでは、実際にデータベースとArticle構造体の間の処理を記述しています。

func GetAllArticles(db *gorm.DB) ([]Article, error) {
	articles := make([]Article, 10)
	if err := db.Find(&articles).Error; err != nil {
		return nil, err
	}
	return articles, nil
}

上の例だと、gorm.DBオブジェクトを使って、Article構造体を複数取得する処理です。
“gormでは、db.Find(&構造体名)”

“select * from (データベース名)”
とほとんど同じ処理をします。
※必ずしもデータベースのテーブルとモデルが一対一で対応するとは限りません。
※deleted_atに時間が入っている場合、論理削除されているものとみなされるため、findで出てきません。

func GetArticleByID(db *gorm.DB, id uint) (*Article, error) {
	article := new(Article)
	if err := db.Where("id = ?", id).First(&article).Error; err != nil {
		return nil, err
	}
	return article, nil
}

次は、where文で条件を絞って、
db.First()で一番最初のデータを取得します。

投稿と記事の削除も同様で

Create…insert into に対応
Delete…update 表名 set deleted_at = xx WHERE 更新する行を特定する条件;
のようになっています。

これで、データベースからデータを取得し、Webサイトで利用する構造体に変換する処理ができました。

これからこの構造体を元にアプリを利用していきます。

※gormでは、構造体を参照渡しする必要があるので、気をつけてください。上手くデータ取得できない場合は、参照渡ししていない時が多くあります。

GORM ガイド
デベロッパーフレンドリーを目指した、素晴らしいGolang用ORMライブラリ。 概要 (ほぼ)フル機能を備えたORM アソシエーション(Has One, Has Many, Belongs To, Many To Many, Polymorphism) フック(Before/After Create/Save/Upda...

↑↑↑↑↑

Gormの使い方

まとめ

今回は、MySQLのテーブル定義からアプリのモデル定義、そして入出力について書きました。

普通、データベースからアプリでデータを扱う場合は、データベースから構造体にマッピングする処理を記述する必要があります。

それをORMであるGormが肩代わりしてくれているので、簡単に処理が書けます。

しかし、ORMはオーバヘッドが大きいので、データ数が多いとスループットが下がりやすい傾向にあります。

 

次は、controllerの説明をしようと思います。

それでは。

 

[ golang ]Go言語入門~ WebAPI ~

タイトルとURLをコピーしました