要做出一個好的 Web Service,對於 資料庫
的熟悉是必須的,因此今天就來聊聊如何透過 golang
對 資料庫
進行存取。
在開始今天的分享前,首先要先安裝 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;
要透過程式語言操作資料庫,最常見的方法就是使用 driver
,golang 原生有提供關於 sql 的抽象介面 database/sql,後來有人利用他封裝了 mysql
的 driver - go-sql-driver,接下來我們會利用這個 package 進行練習。
在使用 driver 之前,我們先透過 go get
的指令進行安裝
go get -u github.com/go-sql-driver/mysql
首先我們要先匯入 database/sql
與 github.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()
}
執行以上的程式如果沒有任何錯誤出現的話,代表已經順利連線到資料庫拉~
既然已經連線到資料庫了,那麼我們就可以開始對資料庫進行互動了,首先我們要先建立一個名為 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
, username
與 password
三個參數,因此我們可以建立一個 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
來與資料庫進行互動吧!