データベースハンドルのオープン

database/sql パッケージは、接続を管理する必要性を軽減することで、データベースアクセスを簡素化します。多くのデータアクセス API とは異なり、database/sql では、明示的に接続を開いて作業を行い、接続を閉じる必要はありません。代わりに、コードは接続プールを表すデータベースハンドルを開き、ハンドルを使用してデータアクセス操作を実行し、取得した行や準備されたステートメントによって保持されているリソースなどを解放する必要がある場合にのみ Close メソッドを呼び出します。

言い換えれば、sql.DB で表されるデータベースハンドルが、コードの代わりに接続を開いたり閉じたりします。コードがハンドルを使用してデータベース操作を実行すると、それらの操作はデータベースに同時アクセスできます。詳細については、接続の管理 を参照してください。

注: データベース接続を予約することもできます。詳細については、専用接続の使用 を参照してください。

database/sql パッケージで利用可能な API に加えて、Go コミュニティは、最も一般的な(そして多くの一般的でない)データベース管理システム(DBMS)のすべてのためのドライバを開発しました。

データベースハンドルを開くときは、次の手順に従います

  1. ドライバを見つける。

    ドライバは、Go コードとデータベース間のリクエストとレスポンスを変換します。詳細については、データベースドライバの検索とインポート を参照してください。

  2. データベースハンドルを開く。

    ドライバをインポートしたら、特定のデータベースのハンドルを開くことができます。詳細については、データベースハンドルのオープン を参照してください。

  3. 接続を確認する。

    データベースハンドルを開いたら、コードは接続が利用可能かどうかを確認できます。詳細については、接続の確認 を参照してください。

コードは通常、データベース接続を明示的に開いたり閉じたりしません。それはデータベースハンドルによって行われます。ただし、コードは、クエリ結果を含む sql.Rows や準備されたステートメントを表す sql.Stmt など、途中で取得したリソースを解放する必要があります。詳細については、リソースの解放 を参照してください。

データベースドライバの検索とインポート

使用している DBMS をサポートするデータベースドライバが必要です。データベースのドライバを見つけるには、SQLDrivers を参照してください。

コードでドライバを使用できるようにするには、他の Go パッケージと同様にインポートします。次に例を示します

import "github.com/go-sql-driver/mysql"

ドライバパッケージから直接関数を呼び出していない場合(たとえば、sql パッケージによって暗黙的に使用されている場合)、インポートパスにアンダースコアを付けたブランクインポートを使用する必要があります。

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

注: ベストプラクティスとして、データベース操作にデータベースドライバ独自の API を使用することは避けてください。代わりに、database/sql パッケージの関数を使用してください。これにより、コードと DBMS の結合を緩やかに保ち、必要に応じて別の DBMS に切り替えることが容易になります。

データベースハンドルのオープン

sql.DB データベースハンドルは、個別に、またはトランザクション内でデータベースを読み書きする機能を提供します。

sql.Open(接続文字列を受け取る)または sql.OpenDBdriver.Connector を受け取る)のいずれかを呼び出すことで、データベースハンドルを取得できます。どちらも sql.DB へのポインタを返します。

注: データベースの認証情報は Go ソースに保存しないでください。詳細については、データベース認証情報の保存 を参照してください。

接続文字列を使用したオープン

接続文字列を使用して接続する場合は、sql.Open 関数 を使用します。文字列の形式は、使用しているドライバによって異なります。

MySQL の例を次に示します

db, err = sql.Open("mysql", "username:password@tcp(127.0.0.1:3306)/jazzrecords")
if err != nil {
    log.Fatal(err)
}

ただし、接続プロパティをより構造化された方法でキャプチャすると、コードがより読みやすくなることがわかるでしょう。詳細はドライバによって異なります。

たとえば、前の例を、MySQL ドライバの Config を使用してプロパティを指定し、FormatDSN メソッド を使用して接続文字列を構築するものに置き換えることができます。

// Specify connection properties.
cfg := mysql.Config{
    User:   username,
    Passwd: password,
    Net:    "tcp",
    Addr:   "127.0.0.1:3306",
    DBName: "jazzrecords",
}

// Get a database handle.
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
    log.Fatal(err)
}

Connector を使用したオープン

接続文字列では使用できないドライバ固有の接続機能を利用する場合は、sql.OpenDB 関数 を使用します。各ドライバは独自の接続プロパティのセットをサポートしており、多くの場合、DBMS 固有の接続リクエストをカスタマイズする方法を提供しています。

前の sql.Open の例を sql.OpenDB を使用するように変更すると、次のようなコードでハンドルを作成できます。

// Specify connection properties.
cfg := mysql.Config{
    User:   username,
    Passwd: password,
    Net:    "tcp",
    Addr:   "127.0.0.1:3306",
    DBName: "jazzrecords",
}

// Get a driver-specific connector.
connector, err := mysql.NewConnector(&cfg)
if err != nil {
    log.Fatal(err)
}

// Get a database handle.
db = sql.OpenDB(connector)

エラー処理

コードは、sql.Open などでハンドルを作成しようとしたときのエラーをチェックする必要があります。これは接続エラーではありません。代わりに、sql.Open がハンドルを初期化できなかった場合にエラーが発生します。これは、たとえば、指定した DSN を解析できない場合に発生する可能性があります。

接続の確認

データベースハンドルを開いても、sql パッケージはすぐに新しいデータベース接続を作成しない場合があります。代わりに、コードが必要になったときに接続を作成する場合があります。データベースをすぐに使用せず、接続を確立できることを確認したい場合は、Ping または PingContext を呼び出します。

次の例のコードは、データベースに ping を送信して接続を確認します。

db, err = sql.Open("mysql", connString)

// Confirm a successful connection.
if err := db.Ping(); err != nil {
    log.Fatal(err)
}

データベース認証情報の保存

データベースの認証情報を Go ソースに保存しないでください。データベースの内容が他人に公開される可能性があります。代わりに、コードの外部に保存するが、コードからアクセスできる方法を見つけてください。たとえば、認証情報を保存し、コードが DBMS で認証するための認証情報を取得するために使用できる API を提供するシークレットキーパーアプリを検討してください。

一般的なアプローチの 1 つは、プログラムが開始される前に、おそらくシークレットマネージャーからロードされたシークレットを環境に保存し、Go プログラムが os.Getenv を使用してそれらを読み取ることです。

username := os.Getenv("DB_USER")
password := os.Getenv("DB_PASS")

このアプローチでは、ローカルテストのために環境変数を自分で設定することもできます。

リソースの解放

database/sql パッケージでは接続を明示的に管理または閉じませんが、コードは不要になったリソースを解放する必要があります。これらには、クエリから返されたデータを表す sql.Rows や準備されたステートメントを表す sql.Stmt によって保持されているリソースが含まれる場合があります。

通常、Close 関数の呼び出しを遅延させることでリソースを閉じ、囲む関数が終了する前にリソースが解放されるようにします。

次の例のコードは、sql.Rows によって保持されているリソースを解放するために Close を遅延させます。

rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// Loop through returned rows.