💥
GoのORM "Bun" を使ってみた
GoのORMで、比較的新しいBunを使ってみたので、その使い方をまとめていこうと思います。
その他のORMは以下の記事が参考になります。
Bunの特徴
Bunは簡単にまとめると「軽い、分かりやすい」が特徴です。
Bun の目標は、慣用的な SQL を記述できるようにすることであり、扱いにくい構文の背後に SQL を隠すことではありません。データベースの CLI (psql など) を使用してクエリの作成とテストを開始し、次に Bun のクエリ ビルダーを使用して結果のクエリを再構築することをお勧めします。
主な機能は次のとおりです。
- 長いクエリを論理的に分離されたブロックに分割します。
- プレースホルダーを適切にエスケープされた値に置き換えます (bun.Identbun.Safe)。
- 構造体ベースのモデルから列のリストといくつかの結合を生成します。
サンプル
本記事のコードは全て以下のリポジトリにまとまっています。
MySQLのdocker-compose.yaml
も置いてあるので、参考にしてみてください。
Bunのインストール
go get github.com/uptrace/bun@latest
DBへの接続
MySQLに接続するコードは以下の通りです。
main.go
// DSNを作成
dsn := fmt.Sprintf(
"%s:%s@tcp(localhost:3306)/%s",
os.Getenv("DB_USER"),
os.Getenv("DB_PASS"),
os.Getenv("DB_NAME"),
)
// DBに接続
sqldb, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
CREATE,INSERT,SELECT
それぞれのメソッドはこちらです。
user.go
package user
import (
"context"
"github.com/uptrace/bun"
)
type User struct {
bun.BaseModel `bun:"table:users,alias:u"`
ID int64 `bun:"id,pk,autoincrement"`
Name string `bun:"name,notnull"`
email string // unexported fields are ignored
}
// Create: テーブルを作成します
//
// すでにテーブルが存在する場合はエラーを返します。
func Create(ctx context.Context, db *bun.DB) {
_, err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
if err != nil {
panic(err)
}
}
// Insert: レコードを挿入します
func (u *User) Insert(ctx context.Context, db *bun.DB) {
_, err := db.NewInsert().Model(u).Exec(ctx)
if err != nil {
panic(err)
}
}
// Select: IDでレコードを取得します
func (u *User) FindByID(ctx context.Context, db *bun.DB) {
if err := db.NewSelect().Model(u).Where("id = ?", u.ID).Scan(ctx); err != nil {
panic(err)
}
}
構造体のタグについて
タグをつけることによって、構造体でテーブルやカラムの設定を記述できます。
以下のページに一覧は載っていますが、いくつか抜粋しておきます(使用する際は公式ページを参照してください)
Tag | Comment |
---|---|
bun.BaseModel bun:"table:table_name"
|
デフォルトのテーブル名をオーバーライドします。 |
bun.BaseModel bun:"alias:table_alias"
|
デフォルトのテーブル別名をオーバーライドします。 |
bun:",pk" | 列を主キーとしてマークし、notnull オプションを適用します。複数の/複合主キーがサポートされています。 |
bun:",notnull" |
CreateTable に NOT NULL 制約を追加するよう指示します。 |
bun:",nullzero" | Go のゼロ値をSQLとしてマーシャルします。NULL またはDEFAULT (サポートされている場合)。 |
実行してみる
main.go
package main
import (
"context"
"database/sql"
"fmt"
"go-bun-sample/user"
"os"
_ "github.com/go-sql-driver/mysql"
"github.com/joho/godotenv"
"github.com/uptrace/bun"
"github.com/uptrace/bun/dialect/mysqldialect"
)
func init() {
if err := godotenv.Load(); err != nil {
panic(err)
}
}
func main() {
// DSNを作成
dsn := fmt.Sprintf(
"%s:%s@tcp(localhost:3306)/%s",
os.Getenv("DB_USER"),
os.Getenv("DB_PASS"),
os.Getenv("DB_NAME"),
)
// DBに接続
sqldb, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
// bunのDBインスタンスを作成
db := bun.NewDB(sqldb, mysqldialect.New())
// 検証用: 最初にテーブルを削除しておく
db.NewDropTable().Model((*user.User)(nil)).Exec(context.Background())
// usersテーブルを作成
user.Create(context.Background(), db)
u1 := &user.User{
ID: 1,
Name: "suzuki",
}
// レコードを作成
u1.Insert(context.Background(), db)
// レコードを取得
u2 := &user.User{ID: 1}
u2.FindByID(context.Background(), db)
fmt.Println(u2.Name) // suzuki
}
DBの中を見てみると、期待通りの値が入っています。
mysql> SELECT * FROM users;
+----+--------+
| id | name |
+----+--------+
| 1 | suzuki |
+----+--------+
githubのリポジトリは今後も変更していく可能性があるので、本記事の内容とずれる場合もあります。
ご承知下さい!
Discussion