tamuraです。
最近、Go言語を始めました。

MySQLにつなぐ

コマンドラインアプリのように1回接続して何か操作して切断、というのであればなんら難しくありません。


package main

import (  
        "database/sql"
        "fmt"

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

func selectSQL(db *sql.DB) {  
        rows, err := db.Query("select * from tbl")
        if err != nil {
                panic(err)
        }
        defer rows.Close()

        for rows.Next() {
                var id string
                var name string
                err := rows.Scan(&id, &name)
                if err != nil {
                    panic(err)
                }
                fmt.Printf("id:%s\\tname:%s\
", id, name)
        }
}

func main() {  
        db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
        if err != nil {
                panic(err)
        }
        defer db.Close()

        selectSQL(db)
}

コネクションプール

Webアプリのような長時間動くアプリを作る場合は、コネクションプールを使用するのが一般的です。
Go言語では自前でコネクションプールを持っていて、自動で勝手にうまくやってくれます。

Javaなどをやっていると誤解しやすいのですが、 sql.Open() で返ってくるのはコネクションではありません。
db.Query等を実行すると、キャッシュされているコネクションがあればそれを使い、コネクションがなければ新たにコネクションを作ってSQLを実行します。 そのため、sql.Open()で返ってきた値をどこからでも参照できるようにしておけば問題ありません。

こんなデータベースパッケージを作りました。

package db

import (  
        "database/sql"

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

var db *sql.DB

// データベースに接続する
func DbInit() (*sql.DB, error) {  
        var err error
        db, err = sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
        return db, err
}

// データベースから切断する
func DbClose() {  
        if db != nil {
                db.Close()
        }
}

// データベースハンドラを取得する
func DbConn() *sql.DB {  
        return db
}

これをメインで呼び出します。

package main

import (  
        "./db"
        "./web"
)

func main() {  
        _, err := db.DbInit()
        if err != nil {
                panic(err)
        }
        defer db.DbClose()

        web.Run()
}

Webアプリではこんな感じで呼び出します。

package web

import (  
        "fmt"
        "log"
        "net/http"

        "github.com/gorilla/mux"

        "../db"
)


func Run() {  
        router := mux.NewRouter().StrictSlash(true)
        router.
                Methods("GET").
                Path("/api/user").
                Handler(http.HandlerFunc(allUser))

        log.Fatal(http.ListenAndServe(":8080", router))
}

func allUser(w http.ResponseWriter, r *http.Request) {  
        db := db.DbConn()
        rows, err := db.Query("select * from tbl")
        if err != nil {
                panic(err)
        }
        defer rows.Close()

        for rows.Next() {
                var id string
                var name string
                err := rows.Scan(&id, &name)
                if err != nil {
                    panic(err)
                }
                fmt.Fprintf(w, "id:%s\\tname:%s\
", id, name)
        }
}

プリペアドステートメント

Java等と同様です。

参照系

db.Prepareして、stmt.Query()します。

func selectSQL(db *sql.DB, id string) {  
        stmt, err := db.Prepare("select * from tbl where id = ?")
        if err != nil {
                panic(err)
        }
        defer stmt.Close()

        rows, err := stmt.Query(id)
        if err != nil {
                panic(err)
        }
        defer rows.Close()

        for rows.Next() {
                var id string
                var name string
                err := rows.Scan(&id, &name)
                if err != nil {
                    panic(err)
                }
                fmt.Printf("id:%s\\tname:%s\
", id, name)
        }
}

更新系

db.Prepareして、stmt.Exec()します。 stmt.Exec()の戻り値からLastInsertId()RowsAffected()を取得することができます。

func updateSQL(db *sql.DB, id string, name string) {  
        stmt, err := db.Prepare("update tbl set name = ? where id = ?")
        if err != nil {
                panic(err)
        }
        defer stmt.Close()

        result, err := stmt.Exec(name, id)
        if err != nil {
                panic(err)
        }

        c, err := result.RowsAffected()
        if err != nil {
                panic(err)
        }

        fmt.Printf("%w行更新しました", c)
}

まとめ

分からなかったらGo言語のソースを見る。
Use the Source, Luke.