Revelでgorpを使ってDBを操作する

# gorp
gorpはgolangのORMライブラリの中で一番人気がありそうな「ORM-ish Library」。

本稿ではgolangのフルスタックWAFのRevel(現在ネイティブでサポートされているORMがない)でgorpを使ってみます。

# gorp controllerの作成
ORMがネイティブでサポートされていないので、DB初期化処理やtransaction処理を自分で書く必要があります。

## app/controllers/init.go
ここにcontrollersのinit関数を書き、gorp controllerのメソッドをOnAppStartやInterceptMethodに登録します。

```go
package controllers

import "github.com/revel/revel"

func init() {
	revel.OnAppStart(InitDB) // DBやテーブルの作成
	revel.InterceptMethod((*GorpController).Begin, revel.BEFORE) // transaction開始
	revel.InterceptMethod((*GorpController).Commit, revel.AFTER) // 変更反映
	revel.InterceptMethod((*GorpController).Rollback, revel.FINALLY) // 異常時処理
}
```

## app/models/user.go
テーブル作りたいので先にモデル定義しときます

```go
package models

type User struct {
	Id   int
	Name string
}
```

## app/controllers/gorp.go
### DB初期化
Userモデルのためのテーブルを作成します
[mattnさんの記事](http://mattn.kaoriya.net/software/lang/go/20120914222828.htm)が参考になります

```go
package controllers

import (
	"database/sql"
	"github.com/coopernurse/gorp"
	_ "github.com/mattn/go-sqlite3"
	"github.com/revel/revel" // あとで使います
	"APP_NAME/app/models" // revel new APP_NAME の APP_NAME
)

var (
	DbMap *gorp.DbMap // このデータベースマッパーからSQLを流す
)

func InitDB() {
	db, err := sql.Open("sqlite3", "./app.db")
	if err != nil {
		panic(err.Error())
	}
	DbMap = &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}

	// ここで好きにテーブルを定義する
	t := DbMap.AddTable(models.User{}).SetKeys(true, "Id")
	t.ColMap("Name").MaxSize = 20

	DbMap.CreateTables()
}
```

### Begin
ここからtransaction処理のためのGorpControllerを定義します
まず`revel.BEFORE`でtransactionを開始する必要があります

```go
type GorpController struct {
	*revel.Controller
	Transaction *gorp.Transaction
}

func (c *GorpController) Begin() revel.Result {
	txn, err := DbMap.Begin() // ここで開始したtransactionをCOMMITする
	if err != nil {
		panic(err)
	}
	c.Transaction = txn
	return nil
}
```

### Commit
`revel.AFTER`でDbMapに登録しておいたSQLをCOMMITします

```go
func (c *GorpController) Commit() revel.Result {
	if c.Transaction == nil {
		return nil
	}
	err := c.Transaction.Commit() // SQLによる変更をDBに反映
	if err != nil && err != sql.ErrTxDone {
		panic(err)
	}
	c.Transaction = nil // 正常終了した場合はROLLBACK処理に入らないようにする
	return nil
}
```

ほぼ例外処理ですね

### Rollback
Commitで問題があった場合`revel.FINALLY`でROLLBACKします

```go
func (c *GorpController) Rollback() revel.Result {
	if c.Transaction == nil {
		return nil
	}
	err := c.Transaction.Rollback() // 問題があった場合変更前の状態に戻す
	if err != nil && err != sql.ErrTxDone {
		panic(err)
	}
	c.Transaction = nil
	return nil
}
```

下準備は完了です。お疲れ様でした。

# SQLを発行してみる
クエリの発行方法は[gorpのAPI doc](http://godoc.org/github.com/coopernurse/gorp)をご覧ください

```go
package controllers

import (
	"fmt"
	"github.com/revel/revel"
	"APP_NAME/app/models"
)

type App struct {
	*revel.Controller
}

func (c App) Index() revel.Result {
	for i := 0; i < 10; i++ {
		DbMap.Insert(&models.User{0, fmt.Sprintf("user%d", i)})
	}

	rows, _ := DbMap.Select(models.User{}, "select * from user")
	for _, row := range rows {
		user := row.(*models.User)
		fmt.Printf("%d, %s\n", user.Id, user.Name)
	}

	return c.Render()
}
```

結果は以下のようになります

```
1, user0
2, user1
3, user2
4, user3
5, user4
6, user5
7, user6
8, user7
9, user8
10, user9
```

無事DBが使えました。