自己紹介
小林と申します。アルバイトとして2019年2月からフューチャーで働いている大学生です。
アルバイトとして入社後は、Goを用いてツール開発、Vue.jsでコンポーネントの開発など沢山のプログラミングの機会を頂いており、日々成長を感じています。現在、GoとDBの連携について学んでおり、沢山の知見を得たため、アウトプットとして、筆を走らせています。
本記事ではGoのDBレイヤーライブラリである、GORM,SQLBoiler,xormの3つについて、それぞれの特徴や違いを押さえられる様まとめてみました。DBレイヤーのライブラリ検討の手がかりとなれば幸いです。
O/Rマッパとは?
Object-Relational-Mapping の頭文字を取った略称です。直訳すると オブジェクト関係マッピング でしょうか。名前だけでは少し分かりにくいのでもう簡単に表現すると、オブジェクト(指向のプログラミング言語)と関係(データベース)のデータの対応付けをしてくれるもの となります。
O/Rマッパライブラリが持つ機能はたくさんありますが、以下のような機能を持つことが多いです。
- DBのRecordとのMapping
- SQL文の組み立て
それでは、GORM, SQLBoiler, xormについて比較していきます。
結果サマリ
AutoMigrationを始めとした各機能の説明は追って説明していきます。
Name | 自動生成の経路 | AutoMigration | Schemaからのリバース | Relation機能のライブラリ提供 |
---|---|---|---|---|
GORM | Struct -> Schema生成 | ○ | - | ○ |
SQLBoiler | Schema -> Struct生成 | - | ○ | - |
XORM | Schema -> Struct生成 | ○ | - | - |
バージョン情報
- Go
v1.12.9
- ライブラリ
- gorm
v1.9.10
- sqlboiler
v3.5.0
- xorm
v0.7.6
- gorm
- PostgreSQL
11.5
- 検証環境は、Docker公式のpostgresコンテナを使用し、
docker run --rm --name db -p 15432:5432 -d postgres:11.5
で起動しています
- 検証環境は、Docker公式のpostgresコンテナを使用し、
比較
GORM
- GORM
- 特徴
- GoのDBレイヤーライブラリとしては最もGitHubのスター数が多い (14500+)
- オートマイグレーション機能がある。 (※後述)
- 所感
- 分かりやすく、直感的で非常に使いやすい
- structは自分で書く必要がある(DBからコードに落としてくれる機能はない)
それではコードレベルで紹介していきます。
テーブル定義
以下のような usersテーブルで存在するとします。
postgres=# \d |
CRUDサンプル
GORM経由でCRUDを行ってみます。
package main |
オートマイグレーション機能
Goの構造体とDBのスキーマを比較して、不足しているものを追加してくれる機能です。DBスキーマを簡単に作成してくれるので非常に有用です。しかし、カラムの削除や変更は出来ません。
例えば、UserテーブルにNameだけでなく年齢(Age)も足したくなった場合、Userの構造体を更新し、db.Automigrate(&User)
とするだけで自動的にカラムが追加されます。ではコードを見ていきましょう。
まず、Structに属性を追加します。
type User struct { |
次に、オートマイグレーションのための AutoMigrate
を呼び出します。
func main() { |
次にDBのスキーマを確認します。
postgres=# \d users |
テーブルにageカラムが追加されていることが分かりました。簡単ですね。
Relation(Association)
GORMは簡単にRelation(Association)を組むことが出来ます。今回はUserがCreditCardを複数枚持つようなHasManyの関係を作ります。
※Userテーブルなしの状態(コンテナ作り立ての状態)からAutoMigrationします(参考)
構造体を以下のように定義します。
type User struct { |
そしてオートマイグレーションを実施してDBのスキーマを更新します。
func main() { |
psqlで結果を確認数と、外部キーは貼られていない気が・・しますね。
postgres=# \d |
リレーションサンプル
ユーザのIDからCreditCardの情報を得てみます。
// INSERT |
GORMの所感
- 全体的に分かりやすく、直感的で非常に使いやすい
- structは自分で書く必要がある(DBからコードに落としてくれる機能はない)
- 記事には載せていませんが、構造体にgorm.Modelを定義することで、ID,CreatedAt,DeletedAtのカラムが追加され、論理削除となる機能もある
- O/Rマッパでこういったレイヤーまでサポートしてくれるのは面白いですね
SQLBoiler
- SQLBoiler
- 特徴
- 高速(らしい) 参考:sqlboilerのベンチマーク
- SQLとの接続部分は自前で実装する必要がある
- DBからコードを自動生成するためにtomlファイルを書く必要がある。
- SQL文の自動生成がメイン機能
テーブル定義
サンプルで用いるusersテーブルとシーケンスです。
primary keyがないテーブルにはSQLBoilerは使用できない(Error: unable to initialize tables: primary key missing in tables
) のでご注意を。
postgres=# \d |
自動生成
SQLBoilerは、DBスキーマとTOMLファイルから生成されたパッケージをインポートして使うのが基本となります。
まずは、プロジェクトのルートにsqlboiler.toml
を置きます。そのtomlファイルにDBの接続先情報やオプションなどの設定を書いていきます。
pkgname="db" |
- TOMLファイルに
add-global-variants=true
を記述すると、グローバルに設定したコネクションを用いたDBの操作メソッドが追加されます。 add-panic-variants=true
を記述すると、error発生時にerrorを返す代わりにpanicを起こすDBの操作メソッドが追加されます。
TOMLを書いたらコード生成に必要なパッケージをインストールしていきます。
go get -u github.com/volatiletech/sqlboiler |
インストールしたらいざコード生成を行います。
sqlboiler --wipe psql |
--wipe
はコード生成前する前にoutputフォルダがあった際、そのフォルダを削除するフラグです。つけておいて損はほぼなさそうです。
生成後のプロジェクトファイル構造はこのようになります。(main.goは自分で作成したものです)
. |
CRUDサンプルコード
自動生成したコードを用いてCRUDアクセスします。
package main |
Relation
コード生成の段階でRelationを貼っておく必要があります。本当は検証のためにGORMのAutoMigrationでサクッと外部キーを貼ろうとしたがAutoMigrationでは貼ってくれないようなので自分でテーブルを作ります。
# create table users ( id serial primary key, name text ); |
上記スキーマでテーブルを作りデータを投入します。
user := db.User{Name: null.StringFrom("hoge"), ID: 3} |
postgres側でレコードの確認します。
postgres=# select * from credit_card; |
適切にレコードが追加されていますね。
以下はdbからuserのレコードを一件持ってきて、userに結びついているcardを持ってくるコードです。
かなり直感的に書けます。コード読んで何しているか分かりやすいです。
users := db.Users().OneGP(context.Background()) |
備考:Tips null.StringFromやnull.IntFromの話
db操作する際にstringを入れる事はできず、null.StringFromを利用する必要がありましたが、なぜそうなっているのか、どのような振る舞いをするのか調査しました。
そこで、定義を見に行きました。
func StringFrom(s string) String |
ぱっと見「???」となるのですがよく見ると、string を引数にして String を返しています。
そして String は以下のように定義されています。
type String struct { |
sql.NullStringを包んでいますね。sql.NullStringの定義を確認してみます。
type NullString struct { |
Validの値を見て値がNULLかどうかを判別しています。
なぜこのような実装になっているかというと、golangにはnilがありますが、pointer型にしか使えないからです。
例えば、stringのゼロ値は””となり、NULLとの区別をつけることが出来ません。
このようにGoの型定義とSQLの型定義には差異があるためその差を埋めるためにnullパッケージが誕生し、それを介することでNULLの表現を可能にしています。
SQLBoiler所感
- contextを明示的に用いているため非同期処理が比較的簡単に出来る。
- tomlにすでに接続先情報が書かれているので正直sql.Openで再度接続先を明記するのは二度手間に感じた。
- Relationは個人的にgormよりも直感的に扱えると感じた。
xorm
- xorm
- 特徴
- 生のSQL実行をサポートしている。
- コマンドラインツールが提供されている
- DBからコードを生成する機能などを持つ
- 生成コードにテスト用コードも付属しているのが良い
テーブル定義
SQLBoilerと同じスキーマを利用します。折角なのでコマンドラインツールを用いてDBから構造体の生成もやります。
まずは go get github.com/go-xorm/cmd/xorm
でインストールします。
その後、このコマンドで構造体を作成します。
cd $GOPATH/src/github.com/go-xorm/cmd/xorm |
./models/users.go
が出来ていると思います。それを自分のプロジェクトフォルダに持ってくればOK。
生成されたusers.goの中身はこちらになりました。
package models |
それではこのコードを用いてCRUDしてみます。
CRUDサンプル
package main |
xorm所感
- xorm.NewEngineでインスタンスを作成してそれを操作していく形で分かりやすい
- リレーションを貼ることが出来ない? (SQLで書く必要がある? 。)ORM Cascading Proposal · Issue #41 · go-xorm/xorm
まとめ
- GoのO/Rマッパである、GORM, SQLBoiler, xormについて比較した
- O/Rマッパと名乗っていても、オートマイグレーション機能や、コードの自動生成機能、論理削除など各ライブラリ特有の差別化要素がある
- 構造体からSchemaを生成してくれるAutoMigrationはとても魅力的な機能ですが、過信せず生成されたDBスキーマを確認する事が大切
今回わたしのPJでは社内ナレッジが蓄積されている点でGORMを採用しました(CRUD操作が直感的で取っ付きやすいので個人的にも良いと思いました)。実際の開発や運用を通して得られたナレッジなどは別途ブログ化したいと思っています。
作成したコード
https://github.com/reud/blog-orm
参考
- もっとORMを使えるようになりたいので、見直してみた - Qiita
- O/Rマッピングの役割とメリット:Hibernateで理解するO/Rマッピング(1) - @IT
- Migration | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
- gormでRelationを組む方法とn+1の回避 - 女子高生になりたい
- volatiletech/sqlboiler: Generate a Go ORM tailored to your database schema.
- jinzhu/gorm: The fantastic ORM library for Golang, aims to be developer friendly
- GolangのORM SQLBoilerを使ってみる - セットアップ編 - ken-aio’s blog
- Has Many | GORM - The fantastic ORM library for Golang, aims to be developer friendly.
- Goの新定番?ORMのSQLBoilerを使ってみる - Qiita