前回は H2 データベースの準備を行いました。
今回はデータベース操作を行っていきます。
ドライバのインポート
postgres のドライバはいろいろありますが、ここでは github.com/lib/pq
を使います。
go get
しましょう。
$ cd $GOPATH $ go get github.com/lib/pq
ドライバが取得できたら、ドライバ github.com/lib/pq
と、組込の database/sql
をインポート指定します。
import ( "database/sql" _ "github.com/lib/pq" )
github.com/lib/pq
はプレースホルダとして _
を指定してインポートします(変数名を指定し、パッケージ名が衝突する場合にエイリアス名を付けたインポートができます)。
Go では未使用の変数があるとコンパイルエラーになります。
インポートしたドライバは直接プログラムから利用しないため、プレースホルダとして _
を指定し、変数を利用しないことを宣言しています。
"github.com/lib/pq"
でパッケージをインポートすると、pq
パッケージの init()
関数が自動的に呼ばれます。
Go では main
関数とinit
関数が特別に予約されており、インポート時にパッケージに属する init()
関数が自動的にコールされる仕組みになっています。
では pq
パッケージにある init()
関数を見てみましょう。
type Driver struct{} func (d *Driver) Open(name string) (driver.Conn, error) { return Open(name) } func init() { sql.Register("postgres", &Driver{}) }
sql.Register()
で postgres
という名前でドライバを登録しています。
ここで Open
関数の頭に見慣れない (d *Driver)
という定義がありますね。
これは Driver
構造体のメソッドとして Open
関数を定義するものとなってます。
関数の頭にレシーバとなるあらゆる型を指定でき、そうすると関数がその型に属するメソッドとして扱うことができるようになります。
データベース接続
sql.Open()
でデータベースコネクションを取得します。
db, err := sql.Open("postgres", "postgres://sa:pass@localhost:5435/~/testdb?sslmode=disable") if err != nil { panic(err) } defer db.Close()
H2 の PG server のポートはデフォルトで 5435
となります。
TCP server running at tcp://localhost:9092 (only local connections) PG server running at pg://localhost:5435 (only local connections) Web Console server running at http://localhost:8082 (only local connections)
データベースファイルは ~/testdb
つまりホームディレクトリに testdb という名前で作成されます。
テーブル作成とレコードの挿入
db.Exec()
にてSQLを実行できます。
db.Exec(` CREATE TABLE users ( id INTEGER auto_increment PRIMARY KEY, name VARCHAR(20), email VARCHAR(255) ); `)
複数行にわたる文字列は で定義できます。ここでは users テーブルを作成しています。
id列は
auto_increment` とすることで自動採番されます。
同様に insert 文は以下のように実行できます。
db.Exec("INSERT INTO users(name, email) VALUES($1, $2);", "Thome", "thome@example.com")
Query
db.Query()
を使います。
rows, err := db.Query(`SELECT id, name, email FROM users`) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var id int var name string var email string rows.Scan(&id, &name, &email) log.Printf("[%v], %v, %v", id, name, email) }
Rows 構造体から Scan()
で結果を取得することができます。
h2パッケージの作成
前回分と合わせて h2
パッケージにまとめます。
h2 というディレクトリの中に h2.go というファイルを作り、以下とします。
package h2 import ( "log" "os" "net/http" "io" "os/exec" "path/filepath" _ "github.com/lib/pq" "database/sql" "time" ) const ( url = "http://central.maven.org/maven2/com/h2database/h2/1.4.194/h2-1.4.194.jar" path = "lib/h2-1.4.194.jar" ) func download(url string, path string) error { out, err := os.Create(path) if err != nil { return err } defer out.Close() resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() if _, err := io.Copy(out, resp.Body); err != nil { return err } return nil } func StartH2() { if _, err := os.Stat(path); os.IsNotExist(err) { os.MkdirAll(filepath.Dir(path), 0755) if err := download(url, path); err != nil { panic(err) } } cmd := exec.Command("java", "-cp", path, "org.h2.tools.Server") cmd.Start() time.Sleep(3 * time.Second) log.Printf("H2 Started. PID[%v]", cmd.Process.Pid) } func StopH2() { err := exec.Command("java", "-cp", path, "org.h2.tools.Server", "-tcpShutdown", "tcp://localhost:9092").Run() if err != nil { log.Fatal(err) } } func InitDb() { db := Db() defer db.Close() db.Exec(` CREATE TABLE users ( id INTEGER auto_increment PRIMARY KEY, name VARCHAR(20), email VARCHAR(255) ); `) } func Db() *sql.DB { db, err := sql.Open("postgres", "postgres://sa:pass@localhost:5435/~/testdb?sslmode=disable") if err != nil { panic(err) } return db }
これで別パッケージから以下のようにデータベースの起動とテーブル生成が出来るようになりました。
h2.StartH2() h2.InitDb()
次回は
blog1.mammb.com