Go言語 ORMライブラリのGORMの簡単な使い方を確認してみました。また、公式ドキュメントにしっかりと使い方が書いてありますので基本的にはそちらを参考にしてもらえればと思います(すべてではないですが日本語訳もされています)。その上でクイックスタートを元に簡単な使い方と挙動を確認してみます。
- インストール
- クイックスタート
- gorm.Model
- SQL実行ログ出力
- created_at、updated_at、deleted_atが不要な場合
- テーブル名を単数形にする場合
- GORM本体のテストカバレッジ
- まとめ
インストール
以下のコマンドでインストールできます。
go get -u github.com/jinzhu/gorm
クイックスタート
公式ドキュメントにあるクイックスタート
を実行してみました。DBだけsqlite
ではなくmysql
に変更しています。
package main
import (
"github.com/jinzhu/gorm"
// _ "github.com/jinzhu/gorm/dialects/sqlite"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func main() {
// db, err := gorm.Open("sqlite3", "test.db")
db, err := gorm.Open("mysql", "gorm:gorm@/sandbox?charset=utf8mb4&parseTime=True&loc=Local")
if err !=nil {
panic("データベースへの接続に失敗しました")
}
defer db.Close()
//スキーマのマイグレーション
db.AutoMigrate(&Product{})
// Create
db.Create(&Product{Code: "L1212", Price: 1000})
// Read
var product Product
db.First(&product, 1) // idが1の製品を探します
db.First(&product, "code = ?", "L1212") // codeがL1212の製品を探します
// Update - 製品価格を2,000に更新します
db.Model(&product).Update("Price", 2000)
// Delete - 製品を削除します
db.Delete(&product)
}
実行してみるとproducts
テーブルが作成され、以下のカラムとレコードができました。struct
では宣言していない、id
、created_at
、updated_at
、deleted_at
カラムができ、deleted_at
に日付が入りソフトデリートが行われています。
go run main.go
gorm.Model
gorm.Model
を宣言するとid
、created_at
、updated_at
、deleted_at
カラムが自動的に注入されます。また,deleted_at
カラムがある場合、Delete
はソフトデリートになります。
SQL実行ログ出力
先ほど実行したプログラムでどんなSQLが実行されたか確認してみます。db.LogMode(true)
を設定するとSQLの実行ログが確認できます。
func main() {
// db, err := gorm.Open("sqlite3", "test.db")
db, err := gorm.Open("mysql", "gorm:gorm@/sandbox?charset=utf8mb4&parseTime=True&loc=Local")
if err !=nil {
panic("データベースへの接続に失敗しました")
}
defer db.Close()
// ログを出力する
db.LogMode(true)
//スキーマのマイグレーション
db.AutoMigrate(&Product{})
// Create
db.Create(&Product{Code: "L1212", Price: 1000})
// Read
var product Product
db.First(&product, 1) // idが1の製品を探します
db.First(&product, "code = ?", "L1212") // codeがL1212の製品を探します
// Update - 製品価格を2,000に更新します
db.Model(&product).Update("Price", 2000)
// Delete - 製品を削除します
db.Delete(&product)
}
実行SQLや実行時間が確認できるようになりました。
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:28)
[2019-06-26 19:57:23] [16.23ms] CREATE TABLE `product` (`id` int unsigned AUTO_INCREMENT,`created_at` timestamp NULL,`updated_at` timestamp NULL,`deleted_at` timestamp NULL,`code` varchar(255),`price` int unsigned , PRIMARY KEY (`id`))
[0 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:28)
[2019-06-26 19:57:23] [1.58ms] CREATE INDEX idx_product_deleted_at ON `product`(deleted_at)
[0 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:31)
[2019-06-26 19:57:23] [0.30ms] INSERT INTO `product` (`created_at`,`updated_at`,`deleted_at`,`code`,`price`) VALUES ('2019-06-26 19:57:23','2019-06-26 19:57:23',NULL,'L1212',1000)
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:35)
[2019-06-26 19:57:23] [0.32ms] SELECT * FROM `product` WHERE `product`.`deleted_at` IS NULL AND ((`product`.`id` = 1)) ORDER BY `product`.`id` ASC LIMIT 1
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:36)
[2019-06-26 19:57:23] [0.33ms] SELECT * FROM `product` WHERE `product`.`deleted_at` IS NULL AND `product`.`id` = 1 AND ((code = 'L1212')) ORDER BY `product`.`id` ASC LIMIT 1
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:39)
[2019-06-26 19:57:23] [0.24ms] UPDATE `product` SET `price` = 2000, `updated_at` = '2019-06-26 19:57:23' WHERE `product`.`deleted_at` IS NULL AND `product`.`id` = 1
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:42)
[2019-06-26 19:57:23] [0.22ms] UPDATE `product` SET `deleted_at`='2019-06-26 19:57:23' WHERE `product`.`deleted_at` IS NULL AND `product`.`id` = 1
[1 rows affected or returned ]
created_at
、updated_at
、deleted_at
が不要な場合
テーブルによってはcreated_at
、updated_at
、deleted_at
が不要な場合もあります。その時はgorm.Model
を使わなければカラムは作られません(IDだけはできるよう個別に記述しています)。
type Product struct {
// gorm.Model
ID uint `gorm:"primary_key"`
Code string
Price uint
}
この場合、先ほどのSQL実行ログは下記のようになります。deleted_at
カラムがないので今回のレコード削除はハードデリートになります。また、DELETE
文でwhere product.id = 1
となっているのは、db.Create(&Product{Code: "L1212", Price: 1000})
実行時のID
を引き継いでいるようで、db.Create(&Product{Code: "L1212", Price: 1000})
を削除するとwhere product.id = 1
の指定はなくなります。もしくはproduct.ID = 2
とDelete
実行前に記述するとwhere product.id = 2
と条件が変わってくれます。
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:31)
[2019-06-26 20:14:50] [0.24ms] INSERT INTO `product` (`code`,`price`) VALUES ('L1212',1000)
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:35)
[2019-06-26 20:14:50] [0.26ms] SELECT * FROM `product` WHERE (`product`.`id` = 1) ORDER BY `product`.`id` ASC LIMIT 1
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:36)
[2019-06-26 20:14:50] [0.25ms] SELECT * FROM `product` WHERE `product`.`id` = 1 AND ((code = 'L1212')) ORDER BY `product`.`id` ASC LIMIT 1
[1 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:39)
[2019-06-26 20:14:50] [0.16ms] UPDATE `product` SET `price` = 2000 WHERE `product`.`id` = 1
[0 rows affected or returned ]
(/Users/masakisato/.go/src/github.com/taisa831/sandbox-gorm/main.go:42)
[2019-06-26 20:14:50] [0.16ms] DELETE FROM `product` WHERE `product`.`id` = 1
[1 rows affected or returned ]
テーブル名を単数形にする場合
マイグレーションを実行すると生成されるテーブル名はproducts
のように自動的に複数系になりますが、複数形にしたくない場合もあります。その時はdb.SingularTable(true)
を宣言するとテーブルが単数形で生成されます。
GORM
本体のテストカバレッジ
OSSライブラリを使う時、どのくらいテストが書かれているか気になったりするので確認してみました。GORM
本体にはtest_all.sh
というテストすべてを実行するスクリプトが用意されているのでそれを元にカバレッジを出してみました。(初期実行時はライブラリが足りないなど出るので都度追加しました。)また、mysql
のテストをする為にmain_test.go
に書かれている以下の記述に事前に設定を合わせておく必要があります。
case "mysql":
fmt.Println("testingmysql...")
if dbDSN == "" {
dbDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True"
}
db, err = gorm.Open("mysql", dbDSN)
今回は、postgres
とmssql
環境がないのでmysql
とsqlite
だけに限定しました。go test
のあとに-coverprofile=cover.out
を追記してカバレッジを出力します。その後go tool cover -html=cover.out -o cover.html
を実行してhtml
に変換しました。
#dialects=("postgres" "mysql" "mssql" "sqlite")
dialects=("mysql" "sqlite")
for dialect in "${dialects[@]}" ; do
echo ${dialect}
DEBUG=false GORM_DIALECT=${dialect} go test -coverprofile=cover.out
done
実行してみると全体ではcoverage: 81.7% of statements
とカバレッジが81.7%でした。
まとめ
非常に簡単なところだけを触っただけで分かりませんが、通して導入しやすく安定したライブラリだと思いました。個人的にORM
はあまり積極的に使わない方ですが簡単に扱える、学習コストが低い、実行SQLを確認しながら扱える、などがあると導入障壁が低くてよいですね。