GOのORMであるgormと、gormの一機能であるgenを使い、DBからデータを取得してみます。
genはテーブル定義から構造体等を自動生成してくれるGORMの機能です。
Gen公式:https://gorm.io/gen/index.html
環境設定
Ubuntuでgorm-sampleを作成して、その中でやっていきます。
$ mkdir gorm-sample
$ cd gorm-sample
localにGOを導入して即席でパスを通します。
$ wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz
$ sudo tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz
$ echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
$ source ~/.bashrc
$ go version
go version go1.24.0 linux/amd64
※aptでインストールするとバージョンが古く、gorm/genの導入時に怒られることがあるので上記のようにしています。
go: golang.org/x/tools@v0.40.0 requires go >= 1.24.0 (running go 1.22.2)
GORMを入れます。
$ go get gorm.io/gorm
$ go get gorm.io/driver/mysql
genも入れます。
$ go get -u gorm.io/gen
動作を確認するために、localDBにusersテーブルを作成します。
mysql> CREATE TABLE users (
-> id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
-> name VARCHAR(255) NOT NULL
-> );
Query OK, 0 rows affected (0.07 sec)
データも投入しておきます。(insert文省略)
mysql> select * from users;
+----+--------+
| id | name |
+----+--------+
| 1 | tarou |
| 2 | hanako |
+----+--------+
2 rows in set (0.00 sec)
genで生成する
gorm-sample/gen/gen.goを作成します。
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gen"
"gorm.io/gorm"
)
func main() {
dsn := "test:testpass@tcp(127.0.0.1:3306)/sample?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
g := gen.NewGenerator(gen.Config{
OutPath: "./query",
ModelPkgPath: "./model",
Mode: gen.WithDefaultQuery,
})
g.UseDB(db)
models := g.GenerateAllTable()
g.ApplyBasic(models...)
g.Execute()
}
genを利用してmodel、queryを生成します。
xxx:~/gorm-sample$ go run gen/gen.go
2025/12/20 21:57:33 find 1 table from db: [users]
2025/12/20 21:57:33 got 2 columns from table <users>
2025/12/20 21:57:33 Start generating code.
2025/12/20 21:57:33 generate model file(table <users> -> {model.User}): /home/xxx/gorm-sample/model/users.gen.go
2025/12/20 21:57:33 generate query file: /home/xxx/gorm-sample/query/users.gen.go
2025/12/20 21:57:33 generate query file: /home/xxx/gorm-sample/query/gen.go
2025/12/20 21:57:33 Generate code done.
動作確認
簡単なWhere句で絞って、レコードを取得してみます。
gorm-sample/main.goを作成します。
package main
import (
"context"
"fmt"
"log"
"example.com/gorm-sample/db"
"example.com/gorm-sample/query"
)
func main() {
fmt.Println("== raw SQL ==")
sqlDB := db.ConnectSQL()
defer sqlDB.Close()
rows, err := sqlDB.Query(
"SELECT id, name FROM users WHERE name = ?",
"hanako",
)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id uint64
var name string
rows.Scan(&id, &name)
fmt.Printf("id=%d name=%s\n", id, name)
}
fmt.Println("\n== gorm/gen ==")
gormDB := db.ConnectGorm()
q := query.Use(gormDB)
ctx := context.Background()
users, err := q.User.WithContext(ctx).Where(q.User.Name.Eq("hanako")).Find()
if err != nil {
log.Fatal(err)
}
for _, u := range users {
fmt.Printf("id=%d name=%s\n", u.ID, u.Name)
}
}
動作確認した結果、レコードを取得できました。
xxx:~/gorm-sample$ go run main.go
== raw SQL ==
id=2 name=hanako
== gorm/gen ==
id=2 name=hanako
感想
・テーブル定義が揺れるシーンで強そう
テーブルに変更が入っても構造体を簡単に修正できます。当然コンパイルエラーになりますので、型やカラム名の修正を取りこぼすことが大きく減りそうです。
開発初期段階や、大人数があちこち触るリプレイスのようなシーンで事故を減らせそうに見えます。
ただし独自の記法があるので、人によっては記述に時間がかかってしまうかもしれません。
・規模が大きくなると扱いが難しそう
ビジネスの規模拡大で複雑なSQLが必要になったとき、結局生SQLに頼らざるを得ないシーンが出てきそうに思います。
また、N+1問題のためにPreload()を使用するなど、パフォーマンス面での問題を解決するのにGORMの知識が必要になっていくようです。
GORM/genだけで最初から最後まで、何から何までというのは難しそうに感じました。
おまけ
local mysqlは以下の手順で準備しています。
WSL2 + Docker ComposeでローカルMySQL環境を構築する

