http://go-database-sql.org/index.html
を読んだ。のでまとめてみた 😃
内容はちょっと古いっぽい。
Overview
sql.DB
はDB connectionではないsql.DB
はインターフェイスの抽象であり、DBの存在であるsql.DB
は次のことをやってくれる- コネクションの管理
- コネクションプールの管理
- コネクションはちゃんと閉じよう
Importing a Database Driver
- driverは直接使うな
Accessing the Database
sql.Open()
は*sql.DB
を返すsql.Open()
ではコネクションは確立しない- 実際に接続が必要になったとき、遅延的に接続される
sql.DB
は長生きするようにデザインされている- 頻繁にOpen/Closeするな
Retrieving Result Sets
database/sql
の関数名は大きな意味を持つ- 例えば
Query
って単語を含んでいれば、DBに問い合わせて行のセットを返す
Fetching Data from the Database
- 結果を受け取るには適切な型の変数のポインタを
rows.Scan()
に渡す db.Query()
で返るrows
はコネクションを占領し続けるので適切にrows.Close()
するrows.Next()
は最終的にはEOFになってrows.Close()
呼ぶけど、ループ途中で抜けたりしたときのためにrows.Close()
は既にクローズ済でも無害- とりあえず
defer rows.Close()
しときましょう - ループの中でクエリして結果を受け取るような場合は、ループの中で
defer
を使わず明示的にrows.Close()
しましょう
How Scan() Works
Scan()
使えばクエリ結果をよろしく型変換してくれる
Preparing Queries
- 何回も同じクエリを実行するときは
db.Prepare()
しましょう
Single-Row Queries
err = db.QueryRow().Scan()
でエラーが起きると、Scan()
までdeferされる
Modifying Data and Using Transactions
Statements that Modify Data
- 行を返さないinsertとかdeleteには
Exec()
を使う Query()
は行を返さないSQLでもsql.Rows
を返し、それは実行後もコネクションを確保している
Working with Transactions
db.Begin()
からCommint()
かRollback()
までが1つのトランザクションとなる- トランザクションの中で
Db
を操作しちゃダメ。Tx
を使う。Tx
はトランザクション内にいるけど、Db
はトランザクション外 - コネクションの状態を変えるような複数のStatementを使う時はトランザクションが必要なくても
Tx
が必要- temporary table
SET @var := value
- charset, timeoutみたいな接続オプションとか
- シングルコネクションでごにょごにょする方法がGoには
Tx
しかない
Using Prepared Statements
Prepared Statements And Connections
- Prepared Statementは、特定のコネクションと紐づく
- database/sqlでは、statementがどのコネクションと紐付いているかを
Stmt
オブジェクトが記憶している Stmt
を実行するとき、Stmt
がそのコネクションを使おうとする- もしコネクションが閉じてたり、busyだった場合は他のコネクションに再度prepareし直す
- こうすることでhigh-concurrencyを実現してる
Avoiding Prepared Statements
db.Query(sql, param)
でも背後でPrepared Statementしてる- Prepared Statementが好ましくないときもある
- RDBMSがサポートしてないとき
- パフォーマンスが厳しいとき(セキュリティは別の方法でなんとかする)
- Prepared Statementしたくないときは、
fmt.Sprint()
とかで整形して、db.Query()
とかに渡す
Prepared Statements in Transactions
Tx
で作られたPrepared StatementsはそのTx
にのみ紐づく- 同様に
DB
上で作られたPrepared Statementはトランザクション内では使えない - トランザクション外で準備されたPrepared Statementを使うには
Tx.Stmt()
を使う - あんま使わない方がいい (今も?)
Parameter Placeholder Syntax
Handling Errors
database/sql
のほぼすべての機能は返り値でエラーを返すから無視すんなよ
Errors From Iterating Resultsets
- ループ後
rows.Err()
で確認する - 異常終了した時自動で
rows.Close()
される
Errors From Closing Resultsets
rows.Next()
が最後までいくと勝手にrows.Close()
されるけど、途中で抜けた時のために明示的にループ後でrows.Close()
するrows.Close()
によるエラーは、何をすべきか明確でないので、ログとったりpanicしたりする
Errors From QueryRow()
QueryRow()
で結果が空のときsql.ErrNoRows
というエラーを返す- 結果が空なのはエラーじゃないことがほとんどなので、適切にハンドルする必要がある
Identifying Specific Database Errors
- エラーのハンドルはエラーメッセージじゃなくエラー番号で
- ドライバごとに方法は異なる
- エラー番号はデータベースによる
- 数字をそのまま使うのは臭うので、定数を使おう
Handling Connection Errors
- 10回を上限に自動で再接続する
Working with NULLs
- NULLABLEな値を扱う時、database/sqlに用意されている特別な型を使う
- トリッキーかつ将来性ないので、あんま使わない方がいい
- デフォルト値を設定した方がいい
- どうしてもNULLを避けられなかったら、SQLで
COALESCE()
を使うのもあり
Working with Unknown Columns
Scan()
は渡す変数の数がクエリ結果の列数と一致してないといけない- クエリ結果の列数がわからないときは、
Columns()
が使える - なんもわからんときは
sql.RawBytes
を使う
The Connection Pool
- デフォルトで接続数に上限はない
db.SetMaxOpenConns(N)
で接続数の上限を設定できる- db.SetMaxIdleConns(N)`はうまく調整する