2012/09/14


そろそろWeb周りだけでなくDB周りも揃ってきて、本格的なアプリケーションが書ける様になってきました。
DBを扱うには、SQLを実行する様な物は標準でサポートしていましたが、gorp を使うと極力SQLを書かずにデータベースを扱う事が出来ます。
coopernurse/gorp - GitHub

Go Relational Persistence I hesitate to call gorp an ORM.

https://github.com/coopernurse/gorp
まずエンティティとなる構造体を宣言します。ここでは Person という構造にします。 type Person struct {
    Id int32
    Name string
}
次に、データベースマッパーを作ります。 db, err := sql.Open("sqlite3""./foo.db")
dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
ドライバはSQLite3を使いましたが、MySQLとPostgreSQLもサポートしている様です。 なお、gorp では使えませんが、Go言語で使えるデータベースは以下のWikiにエントリされています。
SQLDrivers - go-wiki - SQL database drivers - Go Language Community Wiki - Google Project Hosting

SQL database drivers The database/sql and database/sql/driver packages are designed for using databa...

http://code.google.com/p/go-wiki/wiki/SQLDrivers
手前味噌ですが、SQLite3 と Oracle(OCI8)、Microsoft ADODB のドライバは僕が作った奴です。バグあったら教えて下さい。

さて、データベースマッパーが出来たらテーブルと構造体の紐付けを行います。 t := dbmap.AddTableWithName(Person{}, "person").SetKeys(true"Id")
t.ColMap("Id").Rename("id")
t.ColMap("Name").Rename("name")
person というテーブル名で Person 構造体を登録し、Id がキーである事を教えています。その後、カラム名のリネームも行っています。テーブルのフィールド名先頭が大文字とか気持ち悪いとか、UserName というフィールドが "USER_NAME" というカラムに割り当てたい場合に使います。
この後、テーブルを作ります。 dbmap.DropTables()
err = dbmap.CreateTables()
今回は1つしかテーブルを扱っていませんが、一度に複数登録して一括で作成する事が多いと思います。
ではデータを登録しましょう。dbmap.Insert でも登録出来ますが、ここではトランザクションを使いましょう。
tx, _ := dbmap.Begin()
for i := 0; i < 100; i++ {
    tx.Insert(&Person{0, fmt.Sprintf("mattn%03d", i)})
}
tx.Commit()
今度はデータを取得してみましょう。dbmap.Get でキーを指定して1行取得する事も出来ますし、Select を使う事も出来ます。
list, _ := dbmap.Select(Person{}, "select * from person")
for _, l := range list {
    p := l.(*Person)
    fmt.Printf("%d, %s\n", p.Id, p.Name)
}
なんだか便利になってきましたね。全体のコードは以下の通り。
package main

import (
    "database/sql"
    "github.com/coopernurse/gorp"
    _ "github.com/mattn/go-sqlite3"
    "fmt"
)

type Person struct {
    Id int32
    Name string
}

func main() {
    db, err := sql.Open("sqlite3""./foo.db")
    if err != nil {
        panic(err.Error())
    }
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.SqliteDialect{}}
    t := dbmap.AddTableWithName(Person{}, "person").SetKeys(true"Id")
    t.ColMap("Id").Rename("id")
    t.ColMap("Name").Rename("name")
    dbmap.DropTables()
    err = dbmap.CreateTables()
    if err != nil {
        panic(err.Error())
    }

    tx, _ := dbmap.Begin()
    for i := 0; i < 100; i++ {
        tx.Insert(&Person{0, fmt.Sprintf("mattn%03d", i)})
    }
    tx.Commit()

    list, _ := dbmap.Select(Person{}, "select * from person")
    for _, l := range list {
        p := l.(*Person)
        fmt.Printf("%d, %s\n", p.Id, p.Name)
    }
}
Go言語もだんだん仕事で使えそうな言語になってきましたね。

図解そこが知りたいよくわかる実践データベース: 基礎知識からインテグレーション業務に直結した応用知識まで 図解そこが知りたいよくわかる実践データベース: 基礎知識からインテグレーション業務に直結した応用知識まで
弓場 秀樹, 武田 喜美子
翔泳社 単行本 / ¥64 (2001年08月01日)
 
発送可能時間:

Posted at by



2012/09/13


Webのお仕事をやってると、Webサーバのインスタンスと同時にワーカープロセスを起動する事も結構多く、ジョブキューサーバも含めて一括で起動したくなったりします。
ruby 製の foreman という物を使うと Procfile というファイルに書かれた内容でプロセスを起動して、個別に上げたり落としたり出来る様になります。ただしwindowsでは重くて使えない(windows上のrubyどうにかしてる)し、kazeburoさんが作ってるperl製のProcletはParallel::Preforkがwindowsで動かないので困ってました。 ここまで来たら自分で書くしかないかなーとか考えながら、C言語で書くの面倒臭いと思ったのでGo言語で書きました。
mattn/goreman - GitHub

foreman clone written in go language

https://github.com/mattn/goreman
今のところ以下の様に動きます。
まず以下の様なProcfileを用意します。 web1: plackup --port 5000
web2: plackup --port 5001
このファイルのある場所で、 $ goreman start とすると Procfileにエントリされているプロセスを起動します。
goreman1
この様に色付きで標準出力されながら起動します。
別の端末で $ goreman run stop web1 とすると
goreman2
停止し $ goreman run start web1 とすると
goreman3
再度web1に割り当てられたプロセスが起動します。
$ goreman run restart web1 もあります。rpcの警告が出てるのはそのうち直します。
Windows と Linux で動作します。
今のところ、foreman の様に .env ファイルを読み込んだり、.foreman ファイルに従って起動したりはしていませんが、そのうちやるかもしれません。
機能はまだまだですが、そのうち便利になってく気がします。
Posted at by



2012/08/09


某SNSでFizzBuzzが再燃しておりますが、面接時の足きり尺度としてFizzBuzzが使えるかどうかは別として、コードとしてFizzBuzzは有益でもなんでも無いので、FizzBuzzを題材としたコードを晒してみる。 Go言語には、元々Gobと呼ばれるエンコーダが同梱されていて、Goで扱える型をストリーム上に垂れ流しデコードする事が出来る。かつ、RPCも用意されているので小規模なクラサバならば簡単に書けてしまう。
巷のRPCは、エンティティからスタブを作ったり面倒だったりしますが、Go言語ではクライアントとサーバの意識さえ合っていればスタブも必要ない。
まずサーバ package main

import (
    "fmt"
    "net"
    "net/rpc"
)

type FizzBuzz int

func (fb *FizzBuzz) Serve(n int, r *stringerror {
    switch {
    case n % 15 == 0:
        *r = "FizzBuzz"
    case n % 3 == 0:
        *r = "Fizz"
    case n % 5 == 0:
        *r = "Buzz"
    default:
        *r = fmt.Sprintf("%d", n)
    }
    return nil
}

func main(){
    fb := new(FizzBuzz)
    rpc.Register(fb)

    server, _ := net.Listen("tcp"":8001")
    for {
        client, err := server.Accept()
        if err != nil {
            println(err.Error())
            continue
        }
        rpc.ServeConn(client)
    }
}
Register に渡される型のメソッドで、入力引数、戻り値のアドレス の二つを引数に持ち、戻り値に error を持つ物はエクスポートされる。なのでこの場合、n が FizzBuzz の入力、r が戻り値へのアドレスになる。
尚、このRPCは透過性があるので、別に TCP でなくても構わない。

次にクライアント。型がサーバと一致しているのなら、とても簡単に呼び出せる。 package main

import (
    "net/rpc"
)

func main() {
    client, _ := rpc.Dial("tcp""localhost:8001")
    var ret string
    for i := 1; i <= 100; i++ {
        client.Call("FizzBuzz.Serve", i, &ret)
        println(ret)
    }
}
GobというGo言語に特化したエンコーダなので、他の言語でも...という訳には行かないが、ちょっとした物を作る時にはかなり便利である。
尚、Go言語には net/rpc/jsonrpc も含まれていて、こちらを使えばポータビリティのある RPC が書ける様になっている。
Posted at by