Fork me on GitHub

データベースへのアクセス


ドライバーパッケージを読み込んだので、 sql.DB のデータベースオブジェクトを作成する準備が整いました。

sql.DB を作成するためには sql.Open() を使います。これは *sql.DB を返します。

func main() {
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/hello")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
}

注釈

[訳注] 本サイトのコード全体に言えることですが、示されているコードはサンプルなので、実際には log.Fatal(err) などとはせず、適切に return err などして呼び元にエラーを返すことになるでしょう。

例では、いくつかの内容を示しています。

  1. sql.Open への第一引数はドライバー名です。database/sql にドライバーを登録するときに使われる文字列で、混乱を避けるために通常はパッケージ名と同じ名前が用いられます。例えば mysqlgithub.com/go-sql-driver/mysql のドライバーというようにです。いくつかのドライバーは慣例に従っていないため、データベース名を用います。例えば sqlite3github.com/mattn/go-sqlite3 として、 postgresgithub.com/lib/pq として使われます。

  2. 2番目の引数はデータベースへアクセスするためにドライバーに示すドライバー固有の構文の文字列です。上記の例ではローカルの MySQL インスタンスの "hello" データベースに接続しています。

  3. database/sql への操作から返ってきたエラーは確実にチェックし、適切にハンドリングすべきです。

  4. sql.DB が関数のスコープを超えて生存するべきではない場合、 defer db.Close() とするのが Go らしいです。

直感に反しているかもしれませんが、 sql.Open()データベースへのコネクションは確立しません 。またドライバーの接続パラメータを検証しません。代わりに、後で接続するためにデータベースへの抽象化を準備するだけです。データベースへの実際のコネクションは、必要になった最初のときに遅延して、確立します。データベースがアクセス可能であることをすぐに確認する場合(たとえばネットワーク接続を確立してログインする場合)、 db.Ping() を用います。エラーをチェックすることを忘れないでください。

err = db.Ping()
if err != nil {
    // do something here
}

データベースの使用が終わったら Close() することが慣用的ですが、 sql.DB オブジェクトは長期間生存するように設計されています 。データベースに対して頻繁に Open()Close() しないでください。代わりにアクセスする必要があるデータストア 1 つにつき、 sql.DB1つ 生成してください。そして、プログラムがデータストアへのアクセスが完了するまでオブジェクトを保持してください。必要に応じてデータベースオブジェクトを渡したり、何らかの形でグローバルに利用可能にしておきます。データベースアクセスする関数から Open()Close() としないでください。代わりに sql.DB を引数として関数に渡してください。

sql.DB を長時間生存するオブジェクトとして扱わないと、コネクションの再利用や共有の不足、利用可能なネットワークリソースの不足、 多くのTCPコネクションが TIME_WAIT ステータスとして残存することによる、まれな障害を引き起こします。このような問題は意図した設計ではないような database/sql の使い方をしていることを示しています。

さぁ、sql.DB オブジェクトを使いましょう。