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)`はうまく調整する