2023-1-13
Posted by
今回は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/sql
やmattn/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
このドキュメントどう?