Go database/sql tutorial をやった。
database/sql
パッケージはデータベースドライバのパッケージと一緒に使う*sql.DB
型のメソッドは大抵の場合Error
が返り値にあるので、必ずエラーの有無をチェックして適切な処理をすること。無視してはいけないsql.Open()
しただけではデータベースとの接続は行われない。実際にクエリが実行されるとき初めて接続が行われる。クエリ実行前に疎通確認したい場合はdb.Ping()
があるsql.DB
オブジェクトは頻繁にOpen()
,Close()
するものではない- 結果セットを返すクエリは
Query
で始まるメソッドを使う(SELECT など) - 結果セットを返さないクエリは
Exec
で始まるメソッドを使う(INSERT, DELETE など) - 返却された行からカラムのデータを読み込むには
Scan()
を使う - SELECT 文のプレースホルダー記号はデータベース製品によって違う
- MySQL は
WHERE id = ?
- PostgreSQL は
WHERE id = $1
- MySQL は
Scan()
の引数はプレースホルダーの個数と同じでないとエラーになるScan()
は適切な型変換を行う- プリペアドステートメントも使える
- 一行だけしか返さないクエリは
QueryRow()
が使える - トランザクションは
db.Begin()
で開始。tx.Commit()
かtx.Rollback()
で終了 BEGIN
,COMMIT
ステートメントを直接実行せずにdb.Begin()
,tx.Commit()
を使うことTx
オブジェクトと、そのトランザクションが使っているデータベース接続が占有されたままになり、コネクションプールに返却されない
database/sql
パッケージの制約?で、トランザクションは一つのデータベース接続しか使わない- なので、トランザクション内で複数のクエリを連続実行する場合、先に実行したクエリを
Close()
しないといけない- この辺りピンときてない
- なので、トランザクション内で複数のクエリを連続実行する場合、先に実行したクエリを
db.Query()
とtx.Query()
などを混同して使わないこと- トランザクションレベルで実行されるクエリと、別のデータベース接続レベルで実行されるクエリを混同しないこと
QueryRow()
は結果が 0 行のときにsql.ErrNoRows
という特別な型のエラーを返す- 結果が 0 行なのはアプリケーション層にとっては「エラー」ではないが、アプリケーションから判別できるようにするためにあえてそういうデザインにしているらしい
- NULL の可能性があるカラムの値を取得する時は
sql.NullString
のような NULLable な型を使う SELECT * FROM ...
のように返却されるカラム数が不明な場合はColumns()
が使える- が、 Go なり
database/sql
なりの思想として、そういうクエリはなるべく避けて、明示的にカラムを SELECT に列挙するようなのが好ましいとされてそう db.SetMaxIdleConns()
などでコネクションプールの設定を制御できる
- が、 Go なり
いろいろ注意点が多そう。とにかく偏執的なくらいにエラーチェックをすること、トランザクションの使い方などしっかり仕様(ドキュメント)を読み込んで理解して使うこと、などが肝要なようだ。
ある操作をしたい時、やり方がたかだか一通りか二通りくらいしかない、みたいな厳密さは Go そのものの思想なのだろう。窮屈さは感じるものの、手堅さは好ましいとも思う。
写経した内容を https://gitlab.com/kyanny1/go-database-sql-tutorial に置いた。後半は説明だけだったり、実行に適さないコード例ばかりで、写経すべき内容が少なかった。