Go言語でORマップ


Go言語で OR Mapping をしたくなったら、どのライブラリを使うのが良さそうか調べてみた。

## 結論

 現状のアクティブさと、人気度から考えて、[gorp](https://github.com/go-gorp/gorp), [gorm](https://github.com/jinzhu/gorm), [xorm](https://github.com/go-xorm/xorm) の3択

### Railsからの置換えの場合

gormの1択。 
xormもマッパーとかを自前で書けば、いけるかもしれない？

### Goでシンプルなアプリ

structのDBのマップだけしてくれるシンプルなgorpで良さそう。

### Goで大きめのアプリ

xormがセンス良さそう。ただ、
go的じゃないけど、大きめのRails的なので良さそうだったらgorm、
go的にminimalistを通すならgorpもやはり候補に入る感じ。

## [gorp](https://github.com/go-gorp/gorp)

1498 star, 214 fork

基本概念として、StructとDBのマッピングをするだけのシンプルな機構。
複雑なアソシエーションの機構はないが、Transactionや、Before/After Hookなど必要なものは揃っている感じ。

### Freatures

* Bind struct fields to table columns via API or tag
* Support for embedded structs
* Support for transactions
* Forward engineer db schema from structs (great for unit tests)
* Pre/post insert/update/delete hooks
* Automatically generate insert/update/delete statements for a struct
* Automatic binding of auto increment PKs back to struct after insert
* Delete by primary key(s)
* Select by primary key(s)
* Optional trace sql logging
* Bind arbitrary SQL queries to a struct
* Bind slice to SELECT query results without type assertions
* Use positional or named bind parameters in custom SELECT queries
* Optional optimistic locking using a version column (for update/deletes)


## [gorm](https://github.com/jinzhu/gorm)

1727 star, 179 fork


RailsサーバーをGoに置き換えようという場合にすごく便利そうなOR Mapper。

以下に本家に載っているサンプルスキーマを見れば分かる通り、created_at, updated_at, Soft Delete の deleted_at などマップの仕方が、RailsのConventionに従っている。

```
type User struct {
    ID           int
    Birthday     time.Time
    Age          int
    Name         string  `sql:"size:255"` // Default size for string is 255, you could reset it with this tag
    Num          int     `sql:"AUTO_INCREMENT"`
    CreatedAt    time.Time
    UpdatedAt    time.Time
    DeletedAt    *time.Time

    Emails            []Email         // One-To-Many relationship (has many)
    BillingAddress    Address         // One-To-One relationship (has one)
    BillingAddressID  sql.NullInt64   // Foreign key of BillingAddress
    ShippingAddress   Address         // One-To-One relationship (has one)
    ShippingAddressID int             // Foreign key of ShippingAddress
    IgnoreMe          int `sql:"-"`   // Ignore this field
    Languages         []Language `gorm:"many2many:user_languages;"` // Many-To-Many relationship, 'user_languages' is join table
}

type Email struct {
    ID      int
    UserID  int     `sql:"index"` // Foreign key (belongs to), tag `index` will create index for this field when using AutoMigrate
    Email   string  `sql:"type:varchar(100);unique_index"` // Set field's sql type, tag `unique_index` will create unique index
    Subscribed bool
}

type Address struct {
    ID       int
    Address1 string         `sql:"not null;unique"` // Set field as not nullable and unique
    Address2 string         `sql:"type:varchar(100);unique"`
    Post     sql.NullString `sql:"not null"`
}

type Language struct {
    ID   int
    Name string `sql:"index:idx_name_code"` // Create index with name, and will create combined index if find other fields defined same name
    Code string `sql:"index:idx_name_code"` // `unique_index` also works
}
```

他にも、ScopeやCallback、関数もPluck等が存在し、基本的にRailsのActiveRecordをモデルに作られているようなライブラリ。

## [xorm](https://github.com/go-xorm/xorm)

662 star, 131 fork

まだまだ、他のものと比べ人気は少ないが、
http://xorm.io/docs
と割ときちっとしたドキュメントがあり、イケている感がある。

Go的でスマート。Mapperがsnake_caseにするだけか、完全に同一にするだけのみ用意されているので、Railsには合わない。
その分、`has_many`的なアソシエーションのサポートが無かったり、Go的な本当に必要な物だけ作る思想がある気がする。

### Features
- Struct <-> Table Mapping Support
- Chainable APIs
- Transaction Support
- Both ORM and raw SQL operation Support
- Sync database schema Support
- Query Cache speed up
- Database Reverse support, See Xorm Tool README
- Simple cascade loading support
- Optimistic Locking support

という感じで、特にDBからスキーマを作ってくれる機構があるのがすごい。


## Tips: SQL Injection を防ぐ機構

Go言語では、全体として、SQL Injectionを防ぐのは言語で提供するというより、各DBのプレースホルダの機構を使って対応しようという方針らしい。

```
Postgres:
db.Query("select name, phone from contacts where state = $1 and totalSales > $2", "WA", 1000000)

Some other databases:
db.Query("select name, phone from contacts where state = ? and totalSales > ?", "WA", 1000000)
```

- https://groups.google.com/forum/#!topic/golang-nuts/tJhdDtxA0iQ
- https://github.com/jinzhu/gorm/issues/152


## 参考
- [golangでSQLを叩くライブラリまとめ - Qiita](http://qiita.com/umisama/items/c022b16101c48ffdbc6a)
- http://present.go-steel-programmers.org/talk-review-orms/gsp-go-orms.slide