iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 7
0
Modern Web

Go into Web!系列 第 7

Day7 | 使用 GoLang 與資料庫進行互動

要做出一個好的 Web Service,對於 資料庫 的熟悉是必須的,因此今天就來聊聊如何透過 golang資料庫 進行存取。

先備環境

安裝 mysql

在開始今天的分享前,首先要先安裝 mysql,關於 mysql 的安裝可以參考以下連結

建立使用者與資料庫

使用 mysql 指令進入控制台後,建立一個名為 demo 的資料庫

CREATE DATABASE demo CHARACTER SET utf8 COLLATE utf8_general_ci;

建立帳號為 demo,密碼為 demo123

GRANT ALL PRIVILEGES ON demo.* TO 'demo'@'%' IDENTIFIED BY 'demo123'  WITH GRANT OPTION;
FLUSH PRIVILEGES;

操作 mysql

要透過程式語言操作資料庫,最常見的方法就是使用 driver,golang 原生有提供關於 sql 的抽象介面 database/sql,後來有人利用他封裝了 mysql 的 driver - go-sql-driver,接下來我們會利用這個 package 進行練習。

安裝

在使用 driver 之前,我們先透過 go get 的指令進行安裝

go get -u github.com/go-sql-driver/mysql

連線

首先我們要先匯入 database/sqlgithub.com/go-sql-driver/mysql,mysql driver 前面要加上 _,原因是因為要先執行 init 方法進行 sql 註冊

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

接著我們將連線的資訊設定為常數

const (
	USERNAME = "demo1"
	PASSWORD = "demo123"
	NETWORK = "tcp"
	SERVER = "127.0.0.1"
	PORT = 3306
	DATABASE = "demo"
)

再來我們將連線字串拼湊出來

conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",USERNAME, PASSWORD, NETWORK, SERVER, PORT, DATABASE)

之後透過 sql.Open 方法進行連線

db, err := sql.Open("mysql", conn)
if err != nil {
    fmt.Println("開啟 MySQL 連線發生錯誤,原因為:", err)
    return
}

檢查資料庫是否連線正常

if err := db.Ping(); err != nil {
    fmt.Println("資料庫連線錯誤,原因為:", err.Error())
    return
}

最後關閉連線

defer db.Close()

連線的程式最終會像是這樣

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

const (
	USERNAME = "demo1"
	PASSWORD = "demo123"
	NETWORK = "tcp"
	SERVER = "127.0.0.2"
	PORT = 3306
	DATABASE = "demo"
)

func main() {
	conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",USERNAME, PASSWORD, NETWORK, SERVER, PORT, DATABASE)
	db, err := sql.Open("mysql", conn)
	if err != nil {
		fmt.Println("開啟 MySQL 連線發生錯誤,原因為:", err)
		return
	}
    if err := db.Ping(); err != nil {
        fmt.Println("資料庫連線錯誤,原因為:", err.Error())
		return
	}
    defer db.Close()
}

執行以上的程式如果沒有任何錯誤出現的話,代表已經順利連線到資料庫拉~

執行 SQL

建立 Table

既然已經連線到資料庫了,那麼我們就可以開始對資料庫進行互動了,首先我們要先建立一個名為 user 的 table,建立的 SQL 如下

CREATE TABLE IF NOT EXISTS users(
	id INT(4) PRIMARY KEY AUTO_INCREMENT NOT NULL,
	username VARCHAR(64),
	password VARCHAR(64)
);

我們要透過 mysql 的 driver 建立的話,他有內建 Exec 的方法,可以直接執行原生的 SQL 指令,因此我們只要建立一個方法名為 CreateTable,然後把一開始連線建立好的 db 當作參數傳入,之後再利用 Exec 的指令建立 Table 即可

func CreateTable(db *sql.DB) error {
	sql := `CREATE TABLE IF NOT EXISTS users(
	id INT(4) PRIMARY KEY AUTO_INCREMENT NOT NULL,
        username VARCHAR(64),
        password VARCHAR(64)
	); `

	if _, err := db.Exec(sql); err != nil {
		fmt.Println("建立 Table 發生錯誤:", err)
		return err
	}
	fmt.Println("建立 Table 成功!")
	return nil
}

建立完畢之後可以將 CreateTable 的方法於 main 方法呼叫,加入後 main 方法如下

func main() {
	conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", USERNAME, PASSWORD, NETWORK, SERVER, PORT, DATABASE)
	db, err := sql.Open("mysql", conn)
	if err != nil {
		fmt.Println("開啟 MySQL 連線發生錯誤,原因為:", err)
		return
	}
    if err := db.Ping(); err != nil {
        fmt.Println("資料庫連線錯誤,原因為:", err.Error())
		return
	}
	defer db.Close()
	CreateTable(db)
}

執行出來的結果如果輸出 建立 Table 成功! 代表成功的建立 Table 了,我們可以透過任何資料庫管理工具查看結果是否正確。

新增資料

既然都已經建立好 Table 了,那麼我們就來新增資料吧,一樣透過 Exec 的指令即可,新增一筆 user 的 SQL 如下

insert INTO users(username,password) values('test','test');

透過 Exec 的方法執行 insert 指令,Exec 的方法支援將 Value 抽出來作為變數,因此我們可以將程式改為以下

func InsertUser(DB *sql.DB, username, password string) error{
	_,err := DB.Exec("insert INTO users(username,password) values(?,?)",username, password)
	if err != nil{
		fmt.Printf("建立使用者失敗,原因是:%v", err)
		return err
	}
	fmt.Println("建立使用者成功!")
	return nil
}

建立完畢之後可以將 InsertUser 的方法於 main 方法呼叫,加入後 main 方法如下

func main() {
	conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", USERNAME, PASSWORD, NETWORK, SERVER, PORT, DATABASE)
	db, err := sql.Open("mysql", conn)
	if err != nil {
		fmt.Println("開啟 MySQL 連線發生錯誤,原因為:", err)
		return
	}
    if err := db.Ping(); err != nil {
        fmt.Println("資料庫連線錯誤,原因為:", err.Error())
		return
	}
	defer db.Close()

	InsertUser(db, "test", "test")
}

執行出來的結果如果輸出 建立使用者成功! 代表成功的新增資料了,我們可以透過任何資料庫管理工具查看結果是否正確

查詢資料

新增資料後我們就可以透過 SELECT 的 SQL 進行查詢,那麼使用 driver 的時候,他有提供 Query 的語法可以供我們進行查詢,首先,我們提供一個查詢 user 的 SQL 如下

select * from users where username='test';

接著我們要定義搜尋回來的資料結構,user 有 id, usernamepassword 三個參數,因此我們可以建立一個 struct 為

type User struct {
	ID       string
	Username string
	Password string
}

透過 Query 的方法執行 select 指令,Query 的方法支援將 Where 的值抽出來作為變數,因此我們可以將程式改為以下

func QueryUser(db *sql.DB, username string) {
	user := new(User)
	row := db.QueryRow("select * from users where username=?", username)
	if err := row.Scan(&user.ID, &user.Username, &user.Password); err != nil {
		fmt.Printf("映射使用者失敗,原因為:%v\n", err)
		return
	}
	fmt.Println("查詢使用者成功", *user)
}

建立完畢之後可以將 QueryUser 的方法於 main 方法呼叫,加入後 main 方法如下

func main() {
	conn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", USERNAME, PASSWORD, NETWORK, SERVER, PORT, DATABASE)
	db, err := sql.Open("mysql", conn)
	if err != nil {
		fmt.Println("開啟 MySQL 連線發生錯誤,原因為:", err)
		return
	}
    if err := db.Ping(); err != nil {
        fmt.Println("資料庫連線錯誤,原因為:", err.Error())
		return
	}
	defer db.Close()

	CreateTable(db)
	InsertUser(db, "test", "test")
	QueryUser(db, "test")
}

執行出來的結果如果輸出 查詢使用者成功 {1 test test} 代表成功的查詢到資料了,我們可以透過任何資料庫管理工具查看結果是否正確

小結

使用 drive 來操作 mysql 真的是非常的方便,可以直接執行 SQL 語法與資料庫進行互動,但如果要使用 join 或是 巢狀查詢 時或是要預防 SQL Injection 的攻擊等情境時,使用原始的 driver 我們就必須寫更多的程式來處理這些問題,這個時候就有 orm 這種方便的東西產生拉。

明天我們就來聊聊使用 orm 來與資料庫進行互動吧!

參考


上一篇
Day6 | 透過 golang 實作一個簡單的登入功能
下一篇
Day8 | 使用 ORM 與資料庫進行互動
系列文
Go into Web!30

尚未有邦友留言

立即登入留言