2023-1-13

go

Posted by

applemango

今回はgoでmattn/go-sqlite3を使いsqlite3データベースを実装していきます

SQl文を多用しますがgoメインなのでSQL文についての解説をお休みします

インストール

$ go get -u github.com/mattn/go-sqlite3

インポート

import (
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
)

database/sqlはデフォルトのライブラリですが単体では使えません

database/sqlにはデータベースと直接やり取りせずドライバーを通してやり取りします、そのためドライバーが必要で、そのsqlite3のドライバーがgithub.com/mattn/go-sqlite3なのです、直接使う事はありませんが必要不可欠です

dbに接続する

dbに接続するにはsql.Openを使います

閉じるのにはdb.Closeを使います、今回はdeferを使って自動的に閉じるようにしています、せっかくのgoですからね

func main() {
	db, err := sql.Open("sqlite3", "./app.db")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
}

テーブルを作成する

引数や戻り値の無いsql文を実行する時は.Exec単体で実行出来ます

_, err := db.Exec(
	`CREATE TABLE IF NOT EXISTS user (
    id       INTEGER PRIMARY KEY AUTOINCREMENT,
    username STRING UNIQUE NOT NULL,
    password STRING NOT NULL
)`)
if err != nil {
	log.Fatal(err)
}

データを挿入する

今回は.Prepare.Execの二つしか使っていませんがdatabase/sqlmattn/go-sqlite3はトランザクションを使用することを推奨しているような感じがします

stmt, err := db.Prepare(
	`INSERT INTO user ( username, password ) VALUES (?, ?)
`)
if err != nil {
	log.Fatal(err)
}
defer stmt.Close()

_, err = stmt.Exec("apple", "42")
if err != nil {
	log.Fatal(err)
}

_, err = stmt.Exec("osaka", "24")
if err != nil {
	log.Fatal(err)
}

データを取得する

取得する数によって取得方法が変わります

複数取得する方法で一つ取得する事も可能ですが少し面倒です

逆に一つだけ取得する方法で複数取得しようとすると最初の一つだけしか取得することができません

その他にも一つも見つからなかった場合にエラーが出るか出ないかなどの違いもあります(単体の方が出ます)

一つだけ取得する

一つだけの場合は.Prepare,.QueryRowからの.Scanで取得できます

stmt, err := db.Prepare("SELECT id, username, password FROM user WHERE id = ?")
if err != nil {
	log.Fatal(err)
}
defer stmt.Close()

var user User
err = stmt.QueryRow("1").Scan(&user.Id, &user.Username, &user.Password)
if err != nil {
	log.Fatal(err)
}

println(user.Id, user.Username, user.Password)

複数取得する

複数取得する場合は.Queryを使います、また.Queryから値を取り出す場合仮に一つだけ取得する場合でも.Nextを実行する必要があります

rows, err := db.Query("SELECT id, username, password FROM user")
if err != nil {
	log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
	var user User
	err = rows.Scan(&user.Id, &user.Username, &user.Password)
	if err != nil {
		log.Fatal(err)
	}
	println(user.Id, user.Username, user.Password)
}
err = rows.Err()
if err != nil {
	log.Fatal(err)
}

引数を指定する場合はこんな感じ

rows, err := db.Query("SELECT id, username, password FROM user WHERE id = ?", 1)

データを更新する

データを更新する場合でも.Prepareです

stmt, err = db.Prepare("UPDATE user SET username = ? WHERE id = ?")
if err != nil {
	log.Fatal(err)
}
stmt.Exec("mango", "1")
if err != nil {
    log.Fatal(err)
}

データを削除する

データを削除する場合にも.Prepareです

完全にdéjà vu((付けたいんだ許して)

stmt, err = db.Prepare("DELETE FROM user WHERE id = ?")
if err != nil {
	log.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec("2")
if err != nil {
	log.Fatal(err)
}

トランザクション

トランザクションは一つにエラーが出たら全ての変更を破棄したい場合に使う物ですが、これを使うのは簡単でただ単に文の最初に.Begin最後に.Commit.Rollbackを配置するだけです

tx, err := db.Begin()
if err != nil {
	log.Fatal(err)
}

stmt, err := tx.Prepare(`INSERT INTO user ( username, password ) VALUES (?, ?)`)
if err != nil {
	log.Fatal(err)
}
defer stmt.Close()

_, err = stmt.Exec("apple", "42")
if err != nil {
	log.Fatal(err)
}

_, err = stmt.Exec("osaka", "24")
if err != nil {
	log.Fatal(err)
}

err = tx.Commit()
if err != nil {
	log.Fatal(err)
}

終わり

これで一通り出来ました、多分

リンク

mattn/go-sqlite3のpkg.go.dev

database/sqlのpkg.go.dev

このドキュメントどう?

emoji
emoji
emoji
emoji