前回の記事に続いて、下記3つの使用方法についてお見せしたいと思います。
- SelectとGetの違い
- バルクインサート
- トランザクション
前回の記事
始めに、おことわり
- 「使えればいい」レベルでの内容ですので、セキュリティやコード品質は無視しております。ご了承ください。
- クリーンアーキテクチャではなく、ベタベタなController+Model形式での設計であることをご容赦ください。
環境とディレクトリ構成
前回と同じです
例題のソース
1: Select()とGet()の違い
- Select() : 複数行のデータ取得時に使用する。
- Get() : 単数行データ取得時に使用する。
- データが見つからない場合はエラーを出すので注意。
-
SELECT COUNT ~
やSELECT EXISTS ~
のケースに使いやすい。
model\ranks.go
// UseSelect Selectメソッドを使ったデータ取得
func UseSelect(memberId int) ([]MembersResult, error) {
//データベース接続(前回の記事のDB接続ソースを使っています)
db := myDatabase.DbInit()
sql := `SELECT id, name FROM members WHERE id >= ?;`
var member []MembersResult
// Select()は複数行のデータ取得時に使用する。
err := db.Select(&member, sql, memberId)
if err != nil {
return nil, err
}
return member, nil
}
// UseGet Getメソッドを使ったデータ取得
func UseGet(memberId int) (bool, error) {
//データベース接続(前回の記事のDB接続ソースを使っています)
db := myDatabase.DbInit()
sql := `SELECT EXISTS (SELECT id FROM members WHERE id = ?);`
// Get()は単数データ取得時に使用する。
// データが見つからない場合はエラーを出すので注意。
// 「SELECT COUNT」や「SELECT EXISTS」のケースに使いやすい。
var exists bool
err := db.Get(&exists, sql, memberId)
if err != nil {
return false, err
}
return exists, nil
}
2: バルクインサート
プレースフォルダと[]map[string]interface{}
を利用して、バルクインサートができます。
ポイントはSQLで定義したパラメータ名と、[]map[string]interface{}
のインデックス名をもれなく合わせておくことです。
model\ranks.go
// InsertRank() ranksテーブルにバルクインサートを実行する。
func InsertRank() (bool, error) {
//データベース接続
db := myDatabase.DbInit()
now := time.Now()
sql := `INSERT INTO ranks
(id, name, created_at, updated_at)
VALUES
(:id, :name, :cr, :up);`
//SQLで指定されているパラメータと、連想配列の要素をもれなく統一させること。
inserts := []map[string]interface{}{
{"id": 4, "name": "Platinum", "cr": now, "up": now},
{"id": 5, "name": "Diamond", "cr": now, "up": now},
}
// SQLを実行する。
rows, err := db.NamedExec(sql, inserts)
if err != nil {
return false, err
}
// INSERT件数カウント
r, err := rows.RowsAffected()
if err != nil || r < 1 {
return false, err
}
return true, nil
}
3: トランザクション
Beginx()
でトランザクションを開始しsqlx.Txポインタを受け取り、sqlx.Txポインタを元にNamedExec
でSQLを実行します。
処理に失敗した場合はRollback()
を呼び出し、処理に成功した場合はCommit()
します。
model\ranks.go
// DeleteRank() ranksテーブルからデータを2件削除する。トランザクションを用いる。
func DeleteRank() (bool, error) {
//データベース接続
db := myDatabase.DbInit()
sql := `DELETE FROM ranks WHERE name = :del1 OR name = :del2;`
// トランザクション開始(sqlx.Txポインタを受け取る)
transaction, err := db.Beginx()
if err != nil {
return false, err
}
// transactionを元にSQLを実行する。
deletes := map[string]interface{}{
"del1": "Platinum",
"del2": "Diamond",
}
rows, err := transaction.NamedExec(sql, deletes)
if err != nil {
//実行に失敗した場合はロールバックさせ、トランザクションを終了させる。
_ = transaction.Rollback()
return false, err
}
// 成功した場合はコミットさせ、トランザクションを終了させる。
if err := transaction.Commit(); err != nil {
return false, err
}
// DELETE件数カウント
r, err := rows.RowsAffected()
if err != nil || r < 1 {
return false, err
}
return true, nil
}
トランザクション補足
上記の例で使用したBeginx()
のほかにMustBegin()
というメソッドも使えます。
ただしMustBegin()
で始めた場合は、NamedExec()
は使えずMustExec(SQL文)
を使用しなくてはなりません。
(作者のブログ「Transaction」の章より)
以上となります。