DBから直接golangのモデルを生成するxoのご紹介¶
Webアプリを開発している時に、DBのモデル定義の方法にはいろいろなやり方があると思います。
xo は、 DBから直接 golangのモデル定義を自動生成するツールです。
PostgreSQL
MySQL
Oracle
Microsoft SQL Server
SQLite
に対応しており、良く使われるRDBをほぼカバーしていると思います。
インストール¶
goのツールですので、 go get でインストールできます。
$ go get -u golang.org/x/tools/cmd/goimports (依存性のため)
$ go get -u github.com/knq/xo
これで xo というコマンドがインストールされたと思います。
使い方¶
ではさっそく使ってみましょう。使用するDBはPostgreSQLです。
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name TEXT,
age INT NOT NULL,
weight INT,
created_at timestamptz NOT NULL,
updated_at timestamptz
);
CREATE INDEX users_name_idx ON users(name);
このようなテーブルとインデックスがあったとしましょう。
xo を実行します。
$ mkdir -p models # 事前にディレクトリを作っておく
$ xo pgsql://localhost/example -o models
そうすると、models以下に
user.xo.go
xo_db.xo.go
という二つのファイルが作成されます。xoで生成したファイルは *.xo.go となるので分かりやすいですね。
user.xo.goには以下のような内容が生成されています。 NOT NULL をつけたか付けないかで型が違っているところに注目して下さい。また、jsonタグも生成されているので、そのままJSONとして出力もできます。
// User represents a row from 'public.users'.
type User struct {
ID int64 `json:"id"` // id
Name sql.NullString `json:"name"` // name
Age int `json:"age"` // age
Weight sql.NullInt64 `json:"weight"` // weight
CreatedAt *time.Time `json:"created_at"` // created_at
UpdatedAt pq.NullTime `json:"updated_at"` // updated_at
// xo fields
_exists, _deleted bool
}
この生成されたUser型に対して、以下の関数が生成されています。
func (u *User) Exists() bool
func (u *User) Deleted() bool
func (u *User) Insert(db XODB) error
func (u *User) Update(db XODB) error
func (u *User) Save(db XODB) error
func (u *User) Delete(db XODB) error
func (u *User) Upsert(db XODB) error (PostgreSQL 9.5+以上の場合)
XODB型 は xo_db.xo.go で定義されているdbに対するinterfaceです。
IDはPrimary Keyですし、nameに対してindexを貼っています。ということで、以下の二つの関数も生成されています。
func UserByID(db XODB, id int64) (*User, error)
func UsersByName(db XODB, name sql.NullString) ([]*User, error)
これらの関数を使ってSELECTする、という流れです。 UsersByName の方は、返り値がSliceということもポイントですね。
実装¶
ここまで自動生成されていればあとは簡単です。以下のような実装がすぐにできます。
db, err := sql.Open("postgres", "dbname=example sslmode=disable")
if err != nil {
panic(err)
}
now := time.Now()
u := &User{
Age: 18,
CreatedAt: &now,
}
err = u.Insert(db)
if err != nil {
panic(err)
}
user, err := UserByID(db, u.ID) // Insertでu.IDがセットされている
if err != nil {
panic(err)
}
fmt.Println(user.Age) // -> 18が返される
SQL¶
Insert や Update などの関数の中身はどうなってるかというと、
// sql query
const sqlstr = `INSERT INTO public.users (` +
`name, age, weight, created_at, updated_at` +
`) VALUES (` +
`$1, $2, $3, $4, $5` +
`) RETURNING id`
// run query
XOLog(sqlstr, u.Name, u.Age, u.Weight, u.CreatedAt, u.UpdatedAt)
err = db.QueryRow(sqlstr, u.Name, u.Age, u.Weight, u.CreatedAt, u.UpdatedAt).Scan(&u.ID)
if err != nil {
return err
}
というように、SQLがそのまま生成されています。挙動が分かりやすくてぼくはこの方が好きですね。
関数¶
xoが扱うのはテーブル定義だけではありません。関数も扱ってくれます。
CREATE FUNCTION say_hello(text) RETURNS text AS $$
BEGIN
RETURN CONCAT('hello ' || $1);
END;
$$ LANGUAGE plpgsql;
という関数を定義したとしましょう。こうしておくと、 sp_sayhello.xo.go というファイルが生成されます。Stored Procedureですね。
この中には SayHello というgolangの関数が定義されています。
func SayHello(db XODB, v0 string) (string, error)
これ、中身は
// sql query
const sqlstr = `SELECT public.say_hello($1)`
// run query
var ret string
XOLog(sqlstr, v0)
err = db.QueryRow(sqlstr, v0).Scan(&ret)
if err != nil {
return "", err
}
というように、定義した say_hello 関数をSQLで呼ぶようになっています。ですから、
SayHello(db, "hoge")
というように、golangから呼べるようになります。
まとめ¶
DBのメタデータからgolangのコードを生成してくれる、 xo を紹介しました。
この他、PostgreSQLで定義した型をgolangの型に変換してくれるなど、かなりいたれりつくせりです。また、コードはtemplateで作られており、このtemplateは自分で定義することもできるので、SQL文を変えたり、関数を追加したりなども自由にできます。
ちょうど 同じようなもの を作ったのですが、xoの方が断然高機能なので、xoを使ったほうが良いかと思います。
Comments
comments powered by Disqus