golangで、SQLiteを使ってDB構築の手を抜く

2022年1月28日

私が普段使っているDBは、PostgreSQLを使っているのですが、構築手順が面倒くさいし、なによりDBサーバ立てるのを省略したかったです。

ですので、SQLiteを使ってDB構築の手を抜くことにしました。

今日のところは、mattn/go-sqlite3 に _example/simple/simple.go というサンプルファイルがあったので、これを動くことを確認するところまでやりました。

/*
	C:\Users\ebata\kese\gonet-html\1-1
	go get github.com/mattn/go-sqlite3

	どういう訳か、VSCodeのデバッグでトレースができなかったので、fmt.Println("----->10") と入れて確認をしている。
	このプログラムは、

*/

package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	_ "github.com/mattn/go-sqlite3"
)

func main() {
	os.Remove("./foo.db")

	fmt.Println("----->10")

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

	sqlStmt := `
	create table foo (id integer not null primary key, name text);
	delete from foo;
	`
	_, err = db.Exec(sqlStmt)
	if err != nil {
		log.Printf("%q: %s\n", err, sqlStmt)
		return
	}

	fmt.Println("----->9")

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

	fmt.Println("----->8")

	stmt, err := tx.Prepare("insert into foo(id, name) values(?, ?)")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()
	for i := 0; i < 100; i++ {
		_, err = stmt.Exec(i, fmt.Sprintf("こんにちわ世界%03d", i))
		if err != nil {
			log.Fatal(err)
		}
	}
	tx.Commit()

	fmt.Println("----->7")

	rows, err := db.Query("select id, name from foo")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		var id int
		var name string
		err = rows.Scan(&id, &name)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(id, name)
	}

	fmt.Println("----->6")

	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("----->5")

	stmt, err = db.Prepare("select name from foo where id = ?")
	if err != nil {
		log.Fatal(err)
	}
	defer stmt.Close()
	var name string
	err = stmt.QueryRow("3").Scan(&name)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(name)

	fmt.Println("----->4")

	_, err = db.Exec("delete from foo")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("----->3")

	_, err = db.Exec("insert into foo(id, name) values(1, 'foo'), (2, 'bar'), (3, 'baz')")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("----->2")

	rows, err = db.Query("select id, name from foo")
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		var id int
		var name string
		err = rows.Scan(&id, &name)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(id, name)
	}

	fmt.Println("----->1")

	err = rows.Err()
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("----->0")

}

もう一つのサンプルコード

https://qiita.com/geniusmaaakun/items/b3cb44de3b10a526be98 を参照させて頂き、微小修正

package main

/*
C:\Users\ebata\kese\gonet-html\1-1-1
sqliteをインストールする際の手順の確認

# ドライバのインストール
go get github.com/mattn/go-sqlite3

*/

import (
	"database/sql"
	"fmt"
	"log"
	"os"

	//インポート _にしないとコンパイルエラーになる。使用しない為
	_ "github.com/mattn/go-sqlite3"
)

var DbConnection *sql.DB

type Person struct {
	Name string
	Age  int
}

func main() {

	os.Remove("./example.sql") // これを入れないと、DBが太り続ける

	//1
	//DBを開く  なければ作成される
	DbConnection, _ := sql.Open("sqlite3", "./example.sql")
	//終わったら閉じる
	defer DbConnection.Close()
	//DB作成 SQLコマンド
	cmd := `CREATE TABLE IF NOT EXISTS person(
        name STRING,
        age  INT)`

	//実行 結果は返ってこない為、_にする
	_, err := DbConnection.Exec(cmd)

	//エラーハンドリング
	if err != nil {
		fmt.Println("エラー")
		log.Fatalln(err)
	}

	//ターミナル
	//sqlite3
	//.table
	//SELECT * FROM tablename;
	//で確認

	//2 CRUD処理
	//Create
	//データを追加
	//VALUES (?, ?)  値は後で渡す。セキュリテイの関係でこのようにする方がいい
	//SQLインジェクション 悪意あるコマンドでDBが操作されてしまうのを防ぐ ?でエスケープしてくれる
	cmd = "INSERT INTO person (name, age) VALUES (?, ?)"
	//実行
	//レコードを取得する必要のない、クエリはExecメソッドを使う
	//第二引数からは、コマンド?部の値
	_, err = DbConnection.Exec(cmd, "Nancy", 20)
	if err != nil {
		log.Fatalln(err)
	}

	_, err = DbConnection.Exec(cmd, "Mike", 20)
	if err != nil {
		log.Fatalln(err)
	}

	_, err = DbConnection.Exec(cmd, "Tom", 55)
	if err != nil {
		log.Fatalln(err)
	}

	//Update
	//データの更新 Mike が存在する場合
	cmd = "UPDATE person SET age = ? WHERE name = ?"
	_, err = DbConnection.Exec(cmd, 25, "Mike")
	if err != nil {
		log.Fatalln(err)
	}

	//Read
	//Get  All
	//マルチセレクト
	//データ全てをループで表示
	//Queryは全て取得する
	cmd = "SELECT * FROM person"
	rows, _ := DbConnection.Query(cmd)
	defer rows.Close()
	//structを作成
	var pp []Person

	//取得したデータをループでスライスに追加 for rows.Next()
	for rows.Next() {
		var p Person
		//scan データ追加
		err := rows.Scan(&p.Name, &p.Age)
		if err != nil {
			log.Println(err)
		}
		pp = append(pp, p)
	}
	err = rows.Err()
	if err != nil {
		log.Fatalln(err)
	}
	//表示
	for _, p := range pp {
		fmt.Println(p.Name, p.Age)
	}

	//特定のデータを取得
	cmd = "SELECT * FROM person where age = ?"
	//age = 20にして実行
	//QueryRowは最初の一件だけ取得する。

	//row := DbConnection.QueryRow(cmd, 20)
	//row := DbConnection.QueryRow(cmd, 25)
	row := DbConnection.QueryRow(cmd, 55)

	var p Person
	err = row.Scan(&p.Name, &p.Age)
	if err != nil {
		//データがなかったら
		if err == sql.ErrNoRows {
			log.Println("No row")
			//それ以外のエラー
		} else {
			log.Println(err)
		}
	}
	fmt.Println(p.Name, p.Age)

	//Delete
	//データの削除  全て
	cmd = "DELETE FROM person WHERE name = ?"
	_, err = DbConnection.Exec(cmd, "Nancy")
	if err != nil {
		log.Fatalln(err)
	}

	//マルチセレクト  テーブルもSQLインジェクション対策
	//こちらを推奨
	//データを構造体に入れて表示
	//テーブルはSQLインジェクション対策が使えない
	tableName := "person"
	//テーブル名指定は?が使えない為、%sを使う。
	//後に対応されるかも?
	cmd = fmt.Sprintf("SELECT * FROM %s", tableName)
	rows1, _ := DbConnection.Query(cmd)
	defer rows1.Close()
	var pp1 []Person
	//パターンとして覚える
	for rows1.Next() {
		var p Person
		//データを構造体に追加
		err := rows1.Scan(&p.Name, &p.Age)
		if err != nil {
			log.Println(err)
		}
		//ppに追加
		pp1 = append(pp1, p)
	}
	//まとめてエラーチェック
	err = rows1.Err()
	if err != nil {
		//エラーなら終了
		log.Fatalln(err)
	}
	for _, p := range pp1 {
		fmt.Println(p.Name, p.Age)
	}
}

 

2022年1月28日2022/01,江端さんの技術メモ

Posted by ebata