LoginSignup
0

More than 1 year has passed since last update.

(続) Ginフレームワークとsqlxを連携させた簡易プログラム例 (SelectとGetの違い、バルクインサート、トランザクション)

Posted at

前回の記事に続いて、下記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」の章より)


以上となります。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0