SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

EclipseでGoプログラミング! GoClipseのインストールとGojiフレームワークを使ったWeb APIの作成

  • X ポスト
  • このエントリーをはてなブックマークに追加

データベースとSQL

gorpとは

 Webアプリケーションの構築にデータベースは欠かせません。ここでは「gorp」パッケージを利用したデータベースの操作方法について解説します。

 gorpは軽量なORM(Object-Relational Mapping)ライブラリで、ちょうどGojiと net/http の関係と同じように、標準パッケージ「database/sql」に対する薄いラッパーという位置づけになっています。Ruby on RailsのActiveRecordライブラリのように多機能なものではありませんが、その代わりにシンプルで理解が容易です。

gorpを使ってMySQLに接続する

 接続するデータベースに「SQLite」を用いれば解説がしやすいのですが、Windows環境+Go本体だけを前提にするとSQLiteのドライバを導入する手順が煩雑になるため、本稿ではMySQLを使用します。MySQLが稼働しているホスト、データベース名、認証情報については接続が可能であれば任意のもので問題ありません。

 まずはgorp本体とMySQLドライバ「go-sql-driver/mysql」をインストールします。

C:¥> go get -u github.com/go-sql-driver/mysql
C:¥> go get -u github.com/coopernurse/gorp

 

 問題がなければ、データベースへ接続してみます。

package main

import (
    "fmt"
    "log"

    "database/sql"
    "github.com/coopernurse/gorp"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    db, err := sql.Open("mysql", "username:pasword@tcp(localhost:3306)/gorp?charset=utf8&parseTime=true")
    if err != nil {
        log.Fatal(err)
    }
    if err = db.Ping(); err != nil {
        log.Fatal(err)
    }
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}}
    fmt.Printf("%#v", dbmap)
}

 

 接続情報は次のフォーマットで、文字列として与えます。使用する環境に合わせて書き換えてください。

"[ユーザ]:[パスワード]@[接続プロトコル]/[データベース名]?[その他パラメータ]"

 

 sql.Open() にドライバの種類を表す文字列と接続情報文字列を渡すと sql.Db 型の戻り値を得ます。最初のエラーチェックは主にドライバ名などに問題があった場合に対応しています。次に db.Ping() を利用してデータベースに実際に接続できるかのチェックを行っています。いずれの箇所でも、エラーが発生した際には log.Fatal() を呼び出しています。エラーを出力しつつプログラムを中断してくれる便利な書き方です。

 データベース接続の確認がとれたら gorp.DbMap 型を作成します。gorpによるデータベース操作の起点になる構造体です。gorp.DbMap 型に設定している Dialect には、データベースによってそれぞれ異なるSQLの方言を吸収してくれる構造体を渡します。

レコードを作成する

 gorp.DbMap 構造体へテーブル情報を登録します。接続するMySQLには次の構造を持つテーブルがあるものとします。テーブル名は「users」です。

usersテーブルの構造
カラム名 主キー その他
id int(11) ⃝ オートインクリメント
name varchar(255)   NOT NULL
email varchar(255)   NOT NULL
created_at datetime   NOT NULL
updated_at datetime   NOT NULL

 users テーブルのレコードに対応する構造体を定義します。db タグを指定することで、構造体のフィールド名とテーブルのカラム名間の対応関係を定義できます。

type User struct {
    Id int              `db:"id"`
    Name string         `db:"name"`
    Email string        `db:"email"`
    CreatedAt time.Time `db:"created_at"`  // import "time"が必要
    UpdatedAt time.Time `db:"updated_at"`  // 同上
}

 

 gorp.DbMap 構造体へ User 型とテーブルの対応関係を登録します。

dbmap.AddTableWithName(User{}, "users").SetKeys(true, "Id")

 

 上記のコードの gorp.TableMap.Setkeys() の部分は、第1引数で「主キーがオートインクリメンタル(自動採番)である」ことを、第2引数で「主キーのカラム名」に対応する「User 構造体のフィールド名」を指定しています。

 準備は整ったので、まずは users テーブルにレコードを INSERT してみましょう。

    tx, _ := dbmap.Begin()
    err := tx.Insert(&User{Name: "山田太郎", Email: "yamada.taro@example.com", CreatedAt: time.Now(), UpdatedAt: time.Now()})
    if err != nil {
        tx.Rollback()
        log.Fatal(err)
    }
    tx.Commit()

 

 gorp.DbMap.Begin() でトランザクションを開始してローカル変数 txgorp.Transaction 構造体を割り当てています。tx.Insert() へ初期化した User 構造体のポインタを渡すことで自動的に INSERT 文が発行されます。users テーブルの id カラムはオートインクリメントされるため、対応するフィールドの初期化は必要ありません。

 INSERT処理が成功した場合には、tx.Commit() の位置でトランザクションをコミットします。エラーが発生した場合には tx.Rollback() の位置でロールバックを発行しプログラムを中断しています[1]

[1] dbmap.Begin()tx.Commit()tx.Rollback() のいずれの箇所もエラーを返す可能性があります。エラー処理を省略していることに注意してください。

 

SQLのログを確認する

 User 構造体を渡すだけで自動的に INSERT 文が生成されて実行されることは分かりましたが、実際にどのようなSQL文が発行されているのかを確認したいときには、どうすればよいでしょうか。

 gorp内部で生成しているSQLを確認するには、gorp.DbMap.TraceOn() を設定します。次の例では os.Stdout を指定して標準出力に出力させています。

dbmap.TraceOn("[Test]", log.New(os.Stdout, "test:", log.Lmicroseconds))

 

 標準出力以外にも、「log」パッケージの機能を利用して任意のファイルに書き出すこともできます。

SELECT文を発行する

 テーブルへのレコード追加が成功したので、次はクエリを発行してそのレコードを取り出してみましょう。次のように gorp.DbMap.Select()User 構造体型と任意の SELECT 文を与えると、レコードを取り出すことができます。

users, _ := dbmap.Select(User{}, "SELECT * FROM users")
for _, u := range users {
    fmt.Printf("%v¥n", u)
}
// 出力結果
// &{1 山田太郎 yamada.taro@example.com 2014-12-02 08:44:58 +0000 UTC 2014-12-02 08:44:58 +0000 UTC}

 

 この例では発行するSQL文を単純な文字列で与えていますが、SQL文にプレースホルダを設定してパラメータに引数をバインドすることも可能です。むしろ「SQLインジェクション」などの脆弱性を作らないために、こちらの方法を推奨します。

dbmap.Select(User{}, "SELECT * FROM users WHERE id = ?", 1)
// => SELECT * FROM users WHERE id = 1
dbmap.Select(User{}, "SELECT * FROM users WHERE id IN (?)", []int{1,2,3})
// => SELECT * FROM users WHERE id IN (1,2,3)

 

 gorp には、ここまでで紹介した以外にも数多くの機能があります。gorp のトップページに掲示されているドキュメントに主な機能が網羅されているので、参考にしてみてください。

次のページ
Web APIアプリケーションを構築する

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

松尾 愛賀(マツオ アイガ)

株式会社ラクーンの技術戦略部で働くエンジニアです。主に新事業系の技術基盤などを担当してます。RubyとGoとAWSとDebian系ディストロが好み。・株式会社ラクーン技術ブログ:http://techblog.raccoon.ne.jp/

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/8374 2015/01/09 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング