はじめに
Gorm
って使ったことありますか?
Goでは、コードないにsqlクエリを書いてgithub.com/go-sql-driver/mysql
を使えば、データベース操作が行えると思います。
しかし、Djangoなどのmodelを定義するだけでデータベース操作が行えてしまうフレームワークのみを過去に触っていた人は、sqlクエリ
の書き方がわからず苦戦すると思います。
それにいくらsqlが書けたとしても、長々とsql
を書きたくないですよね。
なので、今回はそれを解決するGORM
を使ってみようと思います。
GORMとは
GORM
とは、Go言語のORMライブラリです。
ORMって言われてもわからないですよね。
まずはORMから説明します。
ORM
とは
オブジェクト関係マッピング(Object-Relational Mapper)の略称で、オブジェクト指向におけるクラスや構造体とデータベースを紐付ける技術のことを指します。Djangoでモデルを定義するだけで、データベース操作が行えたのは標準でORMが使われていたからです。
メリット
は
- オブジェクト指向におけるクラスを使った再利用性が高いこと
- クエリの自動生成があるため簡単に操作できる
デメリット
は
- sqlクエリとは異なり、言語やライブラリ毎に知識を得ることが必要
- 複雑な操作はクエリを学ぶ必要がある
という感じだと思います。
個人的には、クエリ書く方がどこでも使えますし、実際にmysql-client
とかでデータベースの中身に入ってデータを確認する時には必要なので好きです。ただ自分はsql書くよりも先にDjangoを触ってORM
によるデータベース操作ORM
の存在を全く理解せずに使って、楽であることは痛感したので良い技術だなと感じています。
環境を作る
Dockerでの環境
今回は、Dockerでmysqlコンテナを立ち上げていこうと思います。
docker-compose.yml
を下のように記述します。
services:
db:
image: mysql:8.0
container_name: mysql_gorm
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: db
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
volumes:
- db-volume:/var/lib/mysql
volumes:
db-volume:
あとは下のコマンドを打ちます。
docker-compose.yml
コンソールに下のような文言があれば立ち上がっています。
mysql_gorm | 2024-08-15T02:58:51.493939Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.38' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
この文言判断しづらいので、mysqlコンテナの中に入ってdbを操作できるか試した方が良さそうです。
GOでの環境
Goでのセットアップを行います。
Goのモジュールを以下のように初期化します。
go mod init <モジュール名>
モジュール名は自由に決めることが可能なので、私は下のようにしました。
go mod init github.com/maooz4426/go_gorm
GORM
をインストールします
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
ここまで来たらコードを書いてみようと思います。
コードを書いてみる
レコードを入れる
下にPersonというスキーマを定義して、それをマイグレート、Jackさんのレコードを挿入した後にJackさんのレコードを取得するようなコードを書いてみました。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type Person struct {
gorm.Model
Name string
Age int
}
func main() {
dsn := "user:password@tcp(localhost:3306)/db?parseTime=true"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("mysql can not open:", err)
}
db.AutoMigrate(&Person{})
db.Create(&Person{
Name: "jack",
Age: 21,
})
var person Person
result := db.First(&person, 1)
if result.Error != nil {
log.Fatal(result.Error)
}
fmt.Println(person.Name)
}
GORMを使う場合、gorm
モジュールと使うデータベースようのドライバーが必要になります。なので今回は、gorm.io/driver/mysql
とgorm.io/gorm
をインポートしています。
DSNをdocker-compose.yml
で書いた環境変数から設定します。
DSN
とは
DataSorceNAMEのことで、データベース接続のために必要な識別子のことです
時間はbyte型で、GORMのcreated_at等の時間に関するカラムはtime.Time型となっていて、型が違うためparseTime=true
をつけないとデータが取れないのでつけてください。
そこからOpenメソッドでデータベースを接続します。
AutoMigrateメソッドでは、マイグレーションを自動で行っています。
マイグレーション
とは
データベースのスキーマ(構造)の更新を行うこと。
例えば、Userテーブルに利き手というカラム(要素)が追加されたら、それをデータベースに適応させる作業のことを言う
Createメソッドでレコードを作成しています。
Firstメソッドでは、レコードの読み込みを行なっており、primary-keyが1であるレコードをPerson型の変数personに読み込んでいます。
Firstメソッドでは、読み取った値を変数に入れて使う必要があります。
実行してみると、
go run main.go
jack
うまく動作しました!
ここでmysqlの中に入ってみようと思います。
Docker Desktopの立ち上げたコンテナを選択して下のような画面に移動します。
このターミナルに下のコマンドでmysqlに入ります。
mysql -h localhost -u user -p
すると下のようにpasswordを入力するように言われるのでdocker-compose.yml
に入力した環境変数のpasswordを使用します。
sh-5.1# mysql -h localhost -u user -p
Enter password:
passwordが入力成功すると下のようになります。
mysql>
そこから下のクエリを入力して行って、テーブルがどうなってるか確認します。
use db
show tables;
すると下のように表示されます。
+--------------+
| Tables_in_db |
+--------------+
| people |
+--------------+
構造体でPersonと指定したのに、なぜかPeopleになっています。
GORMでは標準でテーブル名が複数形になります。
なのでここからレコードを確認するには、Personの複数形であるPeopleをテーブルで指定する必要があります。
mysql> SELECT * FROM people;
+----+-------------------------+-------------------------+-------------------------+------+------+
| id | created_at | updated_at | deleted_at | name | age |
+----+-------------------------+-------------------------+-------------------------+------+------+
| 1 | 2024-08-15 09:58:13.661 | 2024-08-15 10:00:35.084 | 2024-08-15 10:05:54.017 | jack | 21 |
+----+-------------------------+-------------------------+-------------------------+------+------+
レコードを更新する
次にupdateしてみようと思います.
下のようにCreateメソッドをコメントアウトして、名前をJackからKenyに変えるコードを書いてみました。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type Person struct {
gorm.Model
Name string
Age int
}
func main() {
dsn := "user:password@tcp(localhost:3306)/db?parseTime=true"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("mysql can not open:", err)
}
db.AutoMigrate(&Person{})
//db.Create(&Person{
// Name: "jack",
// Age: 21,
//})
db.Model(&Person{}).Where(1).Update("name", "Keny")
var person Person
result := db.First(&person, 1)
if result.Error != nil {
log.Fatal(result.Error)
}
fmt.Println(person.Name)
}
db.Model(&Person{}).Where(1).Update("name", "Keny")
の部分はクエリだと下と同じことを行なっています。
Update people SET name="Keny"
実行結果は下のようになり変わったようです。
go run main.go
Keny
レコードを削除する
次はレコードを削除してみようと思います。
下のコードのように、いくつかコードをコメントアウトして、KenyのレコードをDeleteメソッドで削除しようと思います。
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"log"
)
type Person struct {
gorm.Model
Name string
Age int
}
func main() {
dsn := "user:password@tcp(localhost:3306)/db?parseTime=true"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("mysql can not open:", err)
}
db.AutoMigrate(&Person{})
//db.Create(&Person{
// Name: "jack",
// Age: 21,
//})
//db.Model(&Person{}).Where(1).Update("name", "Keny")
db.Delete(&Person{}, 1)
var person Person
result := db.First(&person, 1)
if result.Error != nil {
log.Fatal(result.Error)
}
fmt.Println(person.Name)
}
実行してみます。
go run main.go
2024/08/15 19:05:54 /Users/maoz/Documents/Go/go_grom/main.go:36 record not found
[0.643ms] [rows:0] SELECT * FROM `people` WHERE `people`.`id` = 1 AND `people`.`deleted_at` IS NULL ORDER BY `people`.`id` LIMIT 1
2024/08/15 19:05:54 record not found
exit status 1
削除できたようです。
最後に
ORMはとても便利で使いやすいのでぜひGoで開発している方は使ってみてください。
自分はsqlの知識が足りないので当分はクエリをコードに書いて行おうと思います。