動かして覚える！Goでデータベースの基本的な接続方法とCRUD操作

# なぜ「動かして覚える！」なのか

自分はどんな技術でも初めて勉強する時、文字を読んだり説明だけされても正直理解できず、実際に自分で書いてみて動きを見てから出ないとなかなか理解しにくいと感じました。

なのでこの記事は、自分のような「文字や説明だけじゃ分からん！」って人向けに実際にコードを書いて動かすことをメインに説明してみました。

データベースはよく使われているMySQLに接続してデータを操作していきます。

Dockerなどのコンテナを利用するか、GoとMySQLが同じPCにインストールされていれば同じように出来るので、良かったらこの記事を見ながらご自身の環境で試してみてください！

この記事の一番最後に丁寧に書かれているMySQLのインストール方法の記事があったので載せておきます。


# この記事の対象者
- **Goの勉強を始めた人**
- **文字や説明だけでは理解しにくいと感じる人**
- **GoでDBをいじってみたい人**
- **基本的なSQLを理解している人**


# 実行環境
- M1 Macbook Air
- macOS Monterey 12.4
- Go 1.18
- MySQL 8.0.28


# SQLにおけるCRUD

| CRUD | SQL  |
|:----:|:----:|
|Create|INSERT|
| Read |SELECT|
| Update |UPDATE|
|Delete|DELETE|



# 事前準備
### Goディレクトリ
testDBという名前のプロジェクトを作成し、その中にこのようなファイルを用意します。
```
.
├── go.mod
├── main.go
└── models
    ├── config.go
    ├── delete.go
    ├── insert.go
    ├── select.go
    └── update.go
```

modelsディレクトリの中のconfig.goにデータベースの接続について書いていきます。
その他のファイルにはデータベース操作について書いていきます。

まずは、コマンドでこの2つのパッケージをインストールします。
- **github.com/go-sql-driver/mysql**
- **github.com/joho/godotenv**

1つ目は、GoでMySQLを使用するためのパッケージです。
2つ目は、.envファイルをGoで読み込むためのパッケージです。(こちらは後で説明します。)
```terminal
testDB % go get github.com/go-sql-driver/mysql
go: added github.com/go-sql-driver/mysql v1.6.0
```

```terminal
testDB % go get github.com/joho/godotenv
go: added github.com/joho/godotenv v1.4.0
```

↓のようなgo.sumファイルが追加されていればOK
```go.sum
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
```
***

### 使用するデータベースとテーブル情報
「test_db」を作成し、その中にIDと名前と年齢の情報があるシンプルなusersテーブルを用意しました。
あらかじめにデータを5つ入れてあります。

```sql
mysql> create database test_db;
Query OK, 1 row affected (0.01 sec)

mysql> use test_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed

mysql> create table users3 (
    ->     id int not null primary key auto_increment,
    ->     name varchar(20) not null,
    ->     age int not null
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> desc users;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int          | NO   | PRI | NULL    | auto_increment |
| name  | varchar(100) | NO   |     | NULL    |                |
| age   | int          | NO   |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> insert into users (name, age) values ('satou', 21),('tanaka', 18),('suzuki', 25),('ito', 30),('satou', 12);
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from users;
+----+--------+-----+
| id | name   | age |
+----+--------+-----+
|  1 | satou  |  21 |
|  2 | tanaka |  18 |
|  3 | suzuki |  25 |
|  4 | ito    |  30 |
|  5 | satou  |  12 |
+----+--------+-----+
```


# 実際にやってみよう

準備が完了したので、実際に書いていきましょう。

## GoからMySQLに接続する

### 環境変数を設定する
まずtestDBディレクトリに「.env」ファイルを作成します。
```terminal
touch .env
```
.envとは、**環境変数をリポジトリにコミットされなかったり**、**環境ごとにアカウントが違ったりコマンドが違った時**に管理出来るファイルのこと。

今回でいうと、MySQLに接続時に使う**アカウント名**や**パスワード**を.envファイルで管理して**他の人に見られないようにする**のに使います。

では、.envファイルに記述しましょう。
```.env
DB_USER=root                # MySQLのアカウント名
DB_PASS=password            # MySQLのパスワード
DB_ADDRESS=localhost:3306   # MySQLのアドレス名
DB_NAME=test_db             # 接続するデータベース名
```
***

### MySQLに接続する
では**models**ディレクトリの**config.go**に記述していきましょう。
```go:models/config.go
package models

import (
	"database/sql"
	"fmt"
	"github.com/go-sql-driver/mysql"
	"github.com/joho/godotenv"
	"os"
)

// DBに接続するための構造体
type Server struct {
	DB *sql.DB
}

func DBConnect() (*sql.DB, error) {
	var err error

	// .envファイルを読み込む
	err = godotenv.Load()
	if err != nil {
		fmt.Println("Error open .env file")
		return nil, err
	}

	// 環境変数を変数に格納する
	dbUser := os.Getenv("DB_USER")
	dbPass := os.Getenv("DB_PASS")
	dbAddr := os.Getenv("DB_ADDRESS")
	dbName := os.Getenv("DB_NAME")

	// 接続プロパティをキャプチャする
	cfg := mysql.Config{
		User:   dbUser,
		Passwd: dbPass,
		Net:    "tcp",
		Addr:   dbAddr,
		DBName: dbName,
	}

	// データベースを開く
	db, err := sql.Open("mysql", cfg.FormatDSN())
	if err != nil {
		fmt.Println("DB open Error")
		return nil, err
	}

	// 接続が有効であるか確認する
	pingErr := db.Ping()
	if pingErr != nil {
		fmt.Println("pingErr")
		return nil, err
	}

	fmt.Println("接続成功！！")
	return db, nil
}
```

**main.go**で「**DBConnect()**」メソッドを呼び出して接続確認してみましょう。
```go:main.go
package main

import (
	"testDB/models"
	"log"
)

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()
}
```

```terminal
testDB % go run main.go
接続成功！！
```
このように出力されていれば接続成功しています。

## MySQLからデータを操作する

接続が成功したので、次はデータベースからデータを取得したり、新しいデータを入れたりなど、色々操作していきます。
modelsディレクトリのそれぞれのgoファイルに書いていきます。

***

### データの取得
- **全てのデータを取得する方法**
- **条件にあったデータのみを取得する方法**
- **ID検索などの単一のデータを取得する方法**

この３つの取得方法を紹介します。
***

#### 全てのデータを取得する

usersテーブルのデータを全て取得する、「**SelectUsers**」メソッドを**select.go**ファイルに作成します。

```go:models/select.go
package models

import (
	"fmt"
)

// Userの構造体
type User struct {
	ID   int
	Name string
	Age  int
}

func (s *Server) SelectUsers() ([]User, error) {
	// 構造体Userのスライス型、users
	var users []User

	// SELECT文を実行する
	rows, err := s.DB.Query("SELECT * FROM users")
	if err != nil {
		return nil, fmt.Errorf("query error: %v", err)
	}

	for rows.Next() {
		var user User
		// データベースから読み取られた列を、
		//sqlパッケージで提供されている、一般的なGoの型に変換する
		if err := rows.Scan(&user.ID, &user.Name, &user.Age); err != nil {
			return nil, fmt.Errorf("scan the user error: %v", err)
		}
		// Usersに追加する
		users = append(users, user)
	}

	// for文でエラーが発生した場合に呼び出される
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("scan users error: %v", err)
	}

	return users, nil
}
```

**main.go**で「**SelectUsers**」メソッドを呼び出して実行してみましょう。
```go:main.go
package main

import (
	"testDB/models"
	"fmt"
	"log"
)

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

    // Serverの構造体を初期化する
    s := models.Server{DB: db}
    // SelectUsersを呼び出す

	users, err := s.SelectUsers()
	if err != nil {
		log.Fatal(err)
	}
	// 結果を出力する
	fmt.Println(users)
}
```
```terminal
testDB % go run main.go
接続成功！！
[{1 satou 21} {2 tanaka 18} {3 suzuki 25} {4 ito 30} {5 satou 12}]
```
***


#### 条件にあったデータのみを取得する
usersテーブルの中の名前が一致したデータを取得する「**SelectUsersByName**」メソッドを作成していきます。
```go:models/select.go
...省略...

func (s *Server) SelectUsersByName(name string) ([]User, error) {
	// 構造体Userのスライス型、users
	var users []User

	// SELECT文を実行する
	rows, err := s.DB.Query("SELECT * FROM users WHERE name = ?", name)
	if err != nil {
		return nil, fmt.Errorf("query error: %v", err)
	}

	for rows.Next() {
		var user User
		// データベースから読み取られた列を、
		//sqlパッケージで提供されている、一般的なGoの型に変換する
		if err := rows.Scan(&user.ID, &user.Name, &user.Age); err != nil {
			return nil, fmt.Errorf("scan user error: %v", err)
		}
		// Usersに追加する
		users = append(users, user)
	}

	// for文でエラーが発生した場合に呼び出される
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("scan users error: %v", err)
	}

	return users, nil
}
```

**main.go**で「**SelectUsersByName**」メソッドを呼び出して実行してみましょう。

```go:main.go
...省略

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}

	name := "satou"
	// SelectUsersByNameを呼び出す
	user, err := s.SelectUsersByName(name)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(user)
}
```
```terminal
testDB % go run main.go
接続成功！！
[{1 satou 21} {5 satou 12}]
```
***

#### 単一のデータを取得する

usersテーブルの中のIDが一致したデータを取得する「**SelectUserByID**」メソッドを作成していきます。

```go:models/select.go
...省略...

func (s *Server) SelectUserByID(id int) (User, error) {
	var user User

	// SELECT文を実行する
	row := s.DB.QueryRow("SELECT * FROM users WHERE id = ?", id)

	// データベースから読み取られた列を、
	//sqlパッケージで提供されている、一般的なGoの型に変換する
	err := row.Scan(&user.ID, &user.Name, &user.Age)
	if err != nil {
		return User{}, fmt.Errorf("scan user error: %v", err)
	}

	return user, nil
}
```

**main.go**で「**SelectUserByID**」メソッドを呼び出して実行してみましょう。

```go:main.go
...省略...

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}

   // SelectUserByIDを呼び出す
	user, err := s.SelectUserByID(3)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(user)
}
```
```terminal
testDB % go run main.go
接続成功！！
{3 suzuki 25}
```

***

### データを挿入する
- **1つのデータを挿入する**
- **複数のデータを挿入する**

この2つを紹介します。

***

#### 1つのデータを挿入する
usersテーブルに新しいデータを入れる、「**InsertUser**」メソッドをinsert.goに作成します。

```go:models/insert.go
package models

import "fmt"

func (s *Server) InsertUser(name string, age int) error {
	// トランザクション開始
	tx, err := s.DB.Begin()
	if err != nil {
		return fmt.Errorf("transaction start error: %v", err)
	}

	// トランザクション中にエラーが発生した場合に確実にロールバックする
	defer tx.Rollback()

	// INSERT文を実行する
	if _, err = tx.Exec("INSERT INTO users (name, age) VALUES (?, ?)", name, age); err != nil {
		return fmt.Errorf("insert user error: %v", err)
	}

	// 成功したらコミットする
	return tx.Commit()
}
```

**main.go**で「**InsertUser**」メソッドを呼び出して実行してみましょう。

```go:main.go
...省略...

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}

	name := "nishida"
	age := 40
	// InsertUserを呼び出す
	err = s.InsertUser(name, age)
	if err != nil {
		log.Fatal(err)
	}
}
```
```terminal
testDB % go run main.go
接続成功！！
```

何もエラーが返ってこなかったので成功したみたいですね。
MySQLか、先ほど作成した、「SelectUsers」メソッドを利用して確認してみましょう。
```sql
mysql> select * from users;
+----+---------+-----+
| id | name    | age |
+----+---------+-----+
|  1 | satou   |  21 |
|  2 | tanaka  |  18 |
|  3 | suzuki  |  25 |
|  4 | ito     |  30 |
|  5 | satou   |  12 |
|  6 | nishida |  40 |
+----+---------+-----+
```

こんな感じに「nishida」が追加されていればOK。

***

#### 複数のデータを挿入する

複数のデータを挿入する、「**InsertUsers**」メソッドをinsert.goに作成します。

```go:models/insert.go
...省略...

func (s *Server) InsertUsers(users []User) error {
	// トランザクション開始
	tx, err := s.DB.Begin()
	if err != nil {
		return fmt.Errorf("transaction start error: %v", err)
	}

	// トランザクション中にエラーが発生した場合に確実にロールバックする
	defer tx.Rollback()

	// プリペアードステートメントを作成
	stmt, err := tx.Prepare("INSERT INTO users (name, age) VALUES (?, ?)")
	if err != nil {
		return fmt.Errorf("prepared statement error: %v", err)
	}
	// 最後にプリペアードステートメントを閉じる
	defer stmt.Close()

	// userを一人一人Insertする
	for _, user := range users {
		// INSERT文を実行する
		if _, err := stmt.Exec(user.Name, user.Age); err != nil {
			return fmt.Errorf("insert user error: %v", err)
		}
	}

	// 成功したらコミットする
	return tx.Commit()
}
```

:::note info
#### プリペアードステートメントについて

**プリペアードステートメント**は、プログラム上でSQL文を動的に生成する必要があるときに、**可変部分**(これ→?, ?)のようにして**あらかじめSQL文を生成しておき**、値の挿入は**処理系**(stmt.Exec())におこなわせること。

元となるSQL文をあらかじめ生成することで、毎回SQL文を呼び出す手間が省けるので、**実行効率を向上させる**ことができる。
:::

**main.go**で「**InsertUsers**」メソッドを呼び出して実行してみましょう。

```go:main.go
...省略...

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}

    // スライス型のusersを作成し、
    // 挿入したいデータを格納する
    users := []models.User{
		{Name: "Nakagawa", Age: 19},
		{Name: "Hashimoto", Age: 34},
		{Name: "Nakamura", Age: 22},
		{Name: "Yokoyama", Age: 56},
	}

    // InsertUsersを呼び出す
	err = s.InsertUsers(users)
	if err != nil {
		log.Fatal(err)
	}
}
```
```terminal
testDB % go run main.go
接続成功！！
```

こちらも何もエラーが返ってこなかったので成功したみたいですね。
MySQLか、先ほど作成した、「SelectUsers」メソッドを利用して確認してみましょう。
```sql
mysql> select * from users;
+----+-----------+-----+
| id | name      | age |
+----+-----------+-----+
|  1 | satou     |  21 |
|  2 | tanaka    |  18 |
|  3 | suzuki    |  25 |
|  4 | ito       |  30 |
|  5 | satou     |  12 |
|  6 | nishida   |  40 |
|  7 | Nakagawa  |  19 |
|  8 | Hashimoto |  34 |
|  9 | Nakamura  |  22 |
| 10 | Yokoyama  |  56 |
+----+-----------+-----+
```

こんな感じに新しく４人追加されていればOK。

***

### データを更新する

データを更新する、「**UpdateUser**」メソッドをupdate.goに作成します。
InsertUserメソッドで作成した方法とほとんど一緒です。

```go:models/update.go
package models

import "fmt"

func (s *Server) UpdateUser(id int, name string) error {
	// トランザクション開始
	tx, err := s.DB.Begin()
	if err != nil {
		return fmt.Errorf("transaction start error: %v", err)
	}

	// トランザクション中にエラーが発生した場合に確実にロールバックする
	defer tx.Rollback()

	// UPDATE文を実行する
	if _, err = tx.Exec("UPDATE users SET name = ? WHERE id = ?", name, id); err != nil {
		return fmt.Errorf("delete user error: %v", err)
	}

	// 成功したらコミットする
	return tx.Commit()
}
```

**main.go**で「**UpdateUser**」メソッドを呼び出して実行してみましょう。

```go:main.go
...省略...

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}
    
    name := "Mike"
	// UpdateUserを呼び出す
	err = s.UpdateUser(1, name)
	if err != nil {
		log.Fatal(err)
	}
}
```
```terminal
testDB % go run main.go
接続成功！！
```

エラーが返ってこなかったので成功したみたいですね。
MySQLか、先ほど作成した、「SelectUserByID」メソッドを利用して確認してみましょう。

```sql
mysql> select * from users where id = 1;
+----+------+-----+
| id | name | age |
+----+------+-----+
|  1 | Mike |  21 |
+----+------+-----+
```
id「1」のデータが「satou」から「Mike」に変わっていればOK。

***

### データを削除する

データを削除する、「**DeleteUser**」メソッドをdelete.goに作成します。
こちらもInsertUserメソッドで作成した方法とほとんど一緒です。

```go:models/delete.go
package models

import "fmt"

func (s *Server) DeleteUser(id int) error {
	// トランザクション開始
	tx, err := s.DB.Begin()
	if err != nil {
		return fmt.Errorf("transaction start error: %v", err)
	}

	// トランザクション中にエラーが発生した場合に確実にロールバックする
	defer tx.Rollback()

	// DELETE文を実行する
	if _, err = tx.Exec("DELETE FROM users WHERE id = ?", id); err != nil {
		return fmt.Errorf("delete user error: %v", err)
	}

	// 成功したらコミットする
	return tx.Commit()
}
```

**main.go**で「**DeleteUser**」メソッドを呼び出して実行してみましょう。

```go:main.go
...省略...

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}
    
    // DeleteUserを呼び出す
	err = s.DeleteUser(1)
	if err != nil {
		log.Fatal(err)
	}
}
```
```terminal
testDB % go run main.go
接続成功！！
```

エラーが返ってこなかったので成功したみたいですね。
MySQLか、先ほど作成した、「SelectUsers」メソッドを利用して確認してみましょう。

```sql
mysql> select * from users;
+----+-----------+-----+
| id | name      | age |
+----+-----------+-----+
|  2 | tanaka    |  18 |
|  3 | suzuki    |  25 |
|  4 | ito       |  30 |
|  5 | satou     |  12 |
|  6 | nishida   |  40 |
|  7 | Nakagawa  |  19 |
|  8 | Hashimoto |  34 |
|  9 | Nakamura  |  22 |
| 10 | Yokoyama  |  56 |
+----+-----------+-----+
```
ちゃんとid「1」のデータが削除されていればOK。

***

## SQL文の「?」について
プリペアードステートメントの部分では、一度可変部分として登場しましたね。ではプリペアードステートメントではなくても使われている理由は一体なんででしょうか？

プリペアードステートメントの時と少し被るのですが、この書き方は**SQLインジェクション**という脆弱性の対策の一つで、**Queryメソッド**や**QueryRowメソッド**、***Execメソッド*などのSQL文では「**?**」と置き、**第2引数以降で受け取りたいデータ**(今回で言うと「idやnameやage」)を入れることで、外部**からのデータの操作を**出来ないようにする**一つの方法です。
***
### SQLインジェクションを体験する
ここで実際にどのように起こるか簡単な事例を試してみましょう。

「**SelectUsersByName**」メソッドを悪さ出来るように書き直してみましょう。
```diff_go:models/select.go
...省略...

func (s *Server) SelectUsersByName(name string) ([]User, error) {
	// 構造体Userのスライス型、users
	var users []User

	// SELECT文を実行する
-	rows, err := s.DB.Query("SELECT * FROM users WHERE name = ?", name)

+   rows, err := s.DB.Query(fmt.Sprintf("SELECT * FROM users WHERE name = '%v'", name))
	if err != nil {
		return nil, fmt.Errorf("query error: %v", err)
	}

	for rows.Next() {
		var user User
		// データベースから読み取られた列を、
		//sqlパッケージで提供されている、一般的なGoの型に変換する
		if err := rows.Scan(&user.ID, &user.Name, &user.Age); err != nil {
			return nil, fmt.Errorf("scan user error: %v", err)
		}
		// Usersに追加する
		users = append(users, user)
	}

	// for文でエラーが発生した場合に呼び出される
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("scan users error: %v", err)
	}

	return users, nil
}
```

fmt.Sprintf()を使用することで、通常のSQL文を実行するようにしました。

では、**main.go**で「**sqlInjectionName**」変数を作成し、「**satou' OR '1' = '1**」を書いて実行してみましょう。


```diff_go:main.go
...省略...

func main() {
	// データベースに接続する
	db, err := models.DBConnect()
	if err != nil {
		log.Fatal(err)
	}
	// 最後に接続を閉じる
	defer db.Close()

	// Serverの構造体を初期化する
	var s = models.Server{DB: db}

+ 	sqlInjectionName := "satou' OR '1' = '1"
 	// SelectUsersByNameを呼び出す
 	user, err := s.SelectUsersByName(sqlInjectionName)
 	if err != nil {
 		log.Fatal(err)
 	}
 	fmt.Println(user)
}
```
```terminal
testDB % go run main.go
接続成功！！
[{2 tanaka 18} {3 suzuki 25} {4 ito 30} {5 satou 12} {6 nishida 40} {7 Nakagawa 19} {8 Hashimoto 34} {9 Nakamura 22} {10 Yokoyama 56}]
```

意図せずに全てのデータが取得されてしまいました。

「**satou' OR '1' = '1**」を実際のSQL文に置き換えると、

「**SELECT * FROM users WHERE name = 'satou' OR '1' = '1'**」
となります。

これは、「nameがsatouに一致するもの、または、1 = 1 は正しい。つまり**TRUEである場合もの**を取得する」
SQLにおいてTRUEはWHERE文を使わないのとほぼ同義です。

もしこれが、住所や電話番号などの**個人情報**を保持するデータだったら、大変なことになってしまいます。

なので基本的には、「**fmt.Sprintf()**」は使わない！
**必ず「?」で書いて、第2引数以降にデータを渡す！！**

# お疲れ様でした
実際にやってみた方、ここまでお疲れ様でした！
これで一通り基本的なデータベースを操作することが出来ると思います！

↓実際に使用したソースコードも貼っておくので良かったら参考にしてみてください！

https://github.com/Rqixy/go-db-test

# まとめ
- データベースに接続するための情報は、**環境変数**に情報を保持する。
- データベースに接続するには、**Openメソッド**を使い、**Pingメソッド**で接続確認をする。
- Selectする場合は、**Queryメソッド**を使う。
- Insert, Update, Deleteをする場合は、**トランザクション**を使用する。
- 複数実行する場合は、プリペアードステートメントを作成する。
- Goからデータを渡す時は、SQL文では「**?**」渡しておいて、**第２引数以降にデータを渡す**。**←SQLインジェクション対策！！**

#

# 参考にしたサイト

https://go.dev/doc/tutorial/database-access

https://pkg.go.dev/database/sql@go1.18.3

https://e-words.jp/w/%E3%83%97%E3%83%AA%E3%83%9A%E3%82%A2%E3%83%89%E3%82%B9%E3%83%86%E3%83%BC%E3%83%88%E3%83%A1%E3%83%B3%E3%83%88.html

https://qiita.com/cotrpepe/items/b8e7f70f27813a846431

https://qiita.com/harutakaoka522/items/e0abddfd0311eb4fb32b

# MySQLインストール参考記事
## Windows
https://www.dbonline.jp/mysql/install/
## Mac
https://chiritsumo-blog.com/mac-mysql-install/

# こちら記事たちもよかったら見てね！

https://qiita.com/Rqixy/items/6bdead71dc02eb233376

https://qiita.com/Rqixy/items/b906fcb54cf162427775

https://qiita.com/Rqixy/items/584c7872799ad3a69d11