2013年3月7日木曜日

Go言語でデータベースを扱う 後編


こんにちは。scarvizです。

今回はMyMySQLを使ってみます。

基本的には下記の内容と同じです。
https://github.com/ziutek/mymysql

環境は前回同様のUbuntu12.04になります。
前回の記事はこちらです→ http://kobegdg.blogspot.jp/2013/02/go.html




■MyMySQLのインストール
MyMySQLは$GOPATH以下に展開されるため、$GOPATHを設定していない場合は、予め設定しておいてください。
$GOPATHの設定については下記を参考にしてください。
「QtでGo言語をするための環境構築」
http://kobegdg.blogspot.jp/2012/08/qtgo.html
「Go言語で独自パッケージを作成する」
http://kobegdg.blogspot.jp/2013/01/go_19.html

次の3つを実行します。

go get github.com/ziutek/mymysql/thrsafe
go get github.com/ziutek/mymysql/autorc
go get github.com/ziutek/mymysql/godrv

■MyMySQLのテスト
MyMySQLのテストには「test」というDBに、ユーザ名「testuser」、パスワード「TestPasswd9」でアクセスして操作していきます。
おそらく「test」というDB名は既に存在しているんじゃないかなと思います。

rootユーザでMySQLに接続し、一度「SHOW DATABASES;」でDB一覧を表示して、「test」が存在していることを確認してください。
もし存在している場合は、「USE test;」で「test」に接続、「SHOW TABLES;」でテーブルが定義されていないことを確認してください。
もし、「test」が存在しない場合は、
mysql> CREATE DATABASE test;

を実行してください。

ユーザを作成します。下記コマンドを実行してください。

mysql> GRANT ALL PRIVILEGES ON test.* TO testuser@localhost;
mysql> SET PASSWORD FOR testuser@localhost = PASSWORD("TestPasswd9");

一度、
mysql> EXIT;

で抜けて、下記コマンドを実行してください。

cd $GOPATH/src/github.com/ziutek/mymysql
sudo gedit /etc/mysql/my.cnf
sudo service mysql restart
./all.bash test

すべて「OK」が表示されていれば、MyMySQLはちゃんとインストールされています。
パッケージも作成されていると思います。

testuserはもう不要なので、削除しても構いません。
MyMySQLのサンプルを試してみたい方はそのままでも良いと思います。

ユーザを削除するには、先にユーザの権限を除いてから削除します。

mysql> REVOKE ALL ON *.* FROM testuser@localhost;
mysql> DROP USER testuser@localhost;

■SQL操作 DB接続
GoからテーブルにSELECT,INSERT,UPDATE,DELETEをしてみたいと思います。
前回作成したユーザがDMLしか扱えないユーザとします。
まずは、CREATE権限があるユーザでDBにテーブルを作成しておきます。

mysql> USE sampledb;
mysql> CREATE TABLE sample_table(id INT AUTO_INCREMENT, name VARCHAR(10), PRIMARY KEY(id));

テーブル一覧を表示してsample_tableが作られているか確認してください。
mysql> SHOW TABLES;

まずはDBに接続します。

package main

import (
 "fmt"
 "github.com/ziutek/mymysql/mysql"
 _ "github.com/ziutek/mymysql/thrsafe"
 "os"
)

/*
エラーを検出する
*/
func checkError(err error) {
 if err != nil {
  fmt.Println(err)
  os.Exit(1)
 }
}

/*
エラーを検出し、実行結果を返す
*/
func checkedResult(rows []mysql.Row, res mysql.Result, err error) ([]mysql.Row, mysql.Result) {
 checkError(err)
 return rows, res
}

func main() {
 proto := "tcp"
 addr := "127.0.0.1:3306"
 dbname := "sampledb"
 table := "sample_table"
 
 var user string
 var pass string
 fmt.Println("ユーザ名を入力してください")
 fmt.Scanln(&user)
 fmt.Println("パスワードを入力してください")
 fmt.Scanln(&pass)

 // 接続情報を作成し、DBに接続する
 db := mysql.New(proto, "", addr, user, pass, dbname)
 checkError(db.Connect())
 fmt.Printf("%sに接続しました\n", dbname)

 // 遅延実行でDBを閉じる
 defer func() {
  checkError(db.Close())
  fmt.Println("接続を閉じました")
 }()
 
 
 // SQLを実行する(※後述)
}

ここでポイントはimportに

_ "github.com/ziutek/mymysql/thrsafe"

を含める点です。
「_」が入っているので、使用はしていないのですが、mysql内で使用しており(※1)、ここでimportしないといけなくなっています。
ただ、こっち側では使用しないパッケージなので、「_」を付けてビルドを通せる形にしています。

ちなみに

_ "github.com/ziutek/mymysql/native"

でも良いみたいです。

あと、checkError関数とcheckedResult関数は自分用にMyMySQLからコピーしてきました。
checkedResult関数はSQL文実行で使用しています。

※1
thrsafe.goのinit関数でNew関数をmysqlの方へ差し込んでいます。
こんな使い方が出来るんですね。

■SQL操作 INSERT文
INSERT文の実行は以下のようにします。

/*
INSERT文を実行する
*/
func execinsert(db mysql.Conn, table string, num int) {
 // INSERT文作成
 sqlstr := "INSERT INTO " + table + " (name) VALUES ('test_%d')"
 // Query実行(パラメータは可変)
 checkedResult(db.Query(sqlstr, num))
 fmt.Println("insertを実行しました")
}

ここでは「test_」に引数のnum(関数呼出し毎にインクリメントしている)を足し合わせたものをnameカラムに設定しています。
SQL文はdb(接続情報)のQuery関数で実行します。第二引数のパラメータは可変になっています。

■SQL操作 SELECT文
SELECT文の実行は以下のようにします。

/*
SELECT文を実行する
*/
func execselect(db mysql.Conn, table string) {
 // SELECT文作成
 sqlstr := "SELECT * FROM " + table
 // Query実行
 rows, res := checkedResult(db.Query(sqlstr))

 // カラムのマッピング
 id := res.Map("id")
 name := res.Map("name")

 fmt.Println("| id | name |")
 for _, row := range rows {
  // 結果を1行ずつ表示する
  fmt.Printf("| %d | %s |\n", row.Int(id), row.Str(name))
 }
}

■SQL操作 UPDATE文
UPDATE文の実行は以下のようにします。

/*
UPDATE文を実行する
*/
func execupdate(db mysql.Conn, table string) {
 // トランザクション開始
 tr, err := db.Begin()
 checkError(err)

 // UPDATE文作成
 sqlstr := "UPDATE " + table + " SET name='test'"
 // UPDATE実行
 _, err = tr.Start(sqlstr)

 if err != nil {
  // UPDATE中に失敗したらロールバックする
  tr.Rollback()
  fmt.Println("Rollbackしました")
 } else {
  // 問題なくUPDATEが完了していればコミットする
  tr.Commit()
  fmt.Println("updateをCommitしました")
 }
}

ここではトランザクションを使用しています。
db(接続情報)のBegin関数を実行し、mysql.Transaction(ここではtr)を取得してください。
SQL文を実行する時にトランザクションのStart関数で実行し、実行に失敗(errがnilでない場合)した場合は、
Rollback関数でロールバックします。
正常にSQL文が実行できたらCommit関数でコミットしてください。

■SQL操作 DELETE文
DELETE文の実行は以下のようにします。

/*
DELETE文を実行する
*/
func execdelete(db mysql.Conn, table string) {
 // DELETE文作成
 sqlstr := "DELETE FROM " + table + " WHERE name='test'"
 // Query実行
 checkedResult(db.Query(sqlstr))
 fmt.Println("deleteを実行しました")
}


これでGoでDBが扱えるようになりましたね!
今回説明したSQL実行関数を http://goo.gl/7R1QR にまとめています。
良かったらご覧ください。

0 件のコメント:

コメントを投稿