簡介
此示例將研究GO語言中MySQL數(shù)據(jù)庫訪問的基礎(chǔ)知識工坊,創(chuàng)建數(shù)據(jù)庫表,存儲和讀取數(shù)據(jù)敢订。
安裝go-sql-driver / mysql軟件包
Go編程語言附帶了一個名為database / sql
的軟件包王污,用于查詢各種SQL數(shù)據(jù)庫。 這很有用楚午,因為它將所有通用SQL功能抽象為一個API供你使用昭齐,但 Go不包括數(shù)據(jù)庫驅(qū)動程序。 在Go中矾柜,數(shù)據(jù)庫驅(qū)動程序是一個用于實現(xiàn)特定數(shù)據(jù)庫底層細節(jié)的軟件包(本例為MySQL)阱驾。由于無法預(yù)見哪些數(shù)據(jù)庫將來都會投入使用,而支持每個可能的數(shù)據(jù)庫將需要大量維護工作怪蔑,因此Go語言未包含數(shù)據(jù)庫驅(qū)動程序里覆,需要額外進行安裝。
要安裝MySQL數(shù)據(jù)庫驅(qū)動程序缆瓣,只需要在終端中執(zhí)行以下操作:
go get -u github.com/go-sql-driver/mysql
連接到MySQL數(shù)據(jù)庫
安裝所有必需的軟件包后喧枷,需要檢查的第一件事是,是否可以成功連接到MySQL數(shù)據(jù)庫弓坞。 如果尚未運行MySQL數(shù)據(jù)庫服務(wù)器隧甚,可以通過Docker輕松的啟動一個實例。 這是MySQL的映像的官方Docker鏡像:https://hub.docker.com/_/mysql
要檢查是否可以連接到數(shù)據(jù)庫昼丑,請導(dǎo)入數(shù)據(jù)庫/ sql和go-sql-driver / mysql軟件包呻逆,并按如下所示打開連接:
import "database/sql"
import _ "go-sql-driver/mysql"
// Configure the database connection (always check errors)
db, err := sql.Open("mysql", "username:password@(127.0.0.1:3306)/dbname?parseTime=true")
// Initialize the first connection to the database, to see if everything works correctly.
// Make sure to check the error.
err := db.Ping()
創(chuàng)建第一個數(shù)據(jù)庫表
我們數(shù)據(jù)庫中的每個數(shù)據(jù)條目都存儲在一個特定的表中。 數(shù)據(jù)庫表由列和行組成菩帝。 這些列為每個數(shù)據(jù)條目提供一個標簽并指定其類型咖城。 這些行是插入的數(shù)據(jù)值。 在我們的第一個示例中呼奢,我們想要創(chuàng)建一個像這樣的表:
id | username | password | created_at |
---|---|---|---|
1 | johndoe | secret | 2019-08-10 12:30:00 |
創(chuàng)建表格的SQL命令如下:
CREATE TABLE users (
id INT AUTO_INCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL,
created_at DATETIME,
PRIMARY KEY (id)
);
現(xiàn)在有了SQL命令宜雀,可以使用database/sql
包在MySQL數(shù)據(jù)庫中創(chuàng)建表:
query := `
CREATE TABLE users (
id INT AUTO_INCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL,
created_at DATETIME,
PRIMARY KEY (id)
);`
// Executes the SQL query in our database. Check err to ensure there was no error.
_, err := db.Exec(query)
插入第一個用戶
如果你熟悉SQL,向表中插入新數(shù)據(jù)就像創(chuàng)建表一樣容易握础。 需要注意的一件事是:默認情況下辐董,Go使用準備好的語句將動態(tài)數(shù)據(jù)插入SQL查詢語句中,這是一種將用戶提供的數(shù)據(jù)安全地傳遞到數(shù)據(jù)庫的方式禀综,而不會造成任何損壞简烘。 在Web編程的早期苔严,程序員將帶有查詢的數(shù)據(jù)直接傳遞到數(shù)據(jù)庫,這導(dǎo)致了巨大的漏洞孤澎,并可能破壞整個Web應(yīng)用程序届氢,請不要那樣做。
要將第一個用戶插入數(shù)據(jù)庫表覆旭,創(chuàng)建一個如下的SQL查詢退子。此處省略了id列,因為它是由MySQL自動設(shè)置的型将。 問號告訴SQL驅(qū)動程序寂祥,它們是實際數(shù)據(jù)的占位符。 在這里可以看到準備好的語句七兜。
INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)
現(xiàn)在可以在Go中使用此語句插入用戶丸凭。
import "time"
username := "johndoe"
password := "secret"
createdAt := time.Now()
// Inserts our data into the users table and returns with the result and a possible error.
// The result contains information about the last inserted id (which was auto-generated for us) and the count of rows this query affected.
result, err := db.Exec(`INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)`, username, password, createdAt)
要獲取用戶在數(shù)據(jù)庫中最新創(chuàng)建的ID,只需按以下方式獲染:
userID, err := result.LastInsertId()
查詢用戶表
現(xiàn)在表中有一個用戶贮乳,我們想查詢它并獲取其所有信息。 在Go中有兩種查詢表的方法恬惯。 db.Query
可以迭代查詢多行;還有db.QueryRow
只查詢特定的行亚茬。
查詢特定行的工作原理基本上與之前介紹的所有其他SQL命令一樣酪耳。
SQL命令通過其ID查詢單個用戶,如下所示:
SELECT id, username, password, created_at FROM users WHERE id = ?
在Go中首先聲明一些變量來存儲數(shù)據(jù)刹缝,然后查詢單個數(shù)據(jù)庫行碗暗,如下所示:
var (
id int
username string
password string
createdAt time.Time
)
// Query the database and scan the values into out variables. Don't forget to check for errors.
query := `SELECT id, username, password, created_at FROM users WHERE id = ?`
err := db.QueryRow(query, 1).Scan(&id, &username, &password, &createdAt)
查詢所有用戶
前面的部分介紹了如何查詢單個用戶行。 而許多應(yīng)用程序都有查詢所有用戶的應(yīng)用梢夯。 這與上面的示例相似言疗,只是涉及更多的編碼。
我們可以使用上面示例中的SQL命令并修改WHERE
子句颂砸。 這樣可以查詢所有用戶噪奄。
SELECT id, username, password, created_at FROM users
在Go中首先聲明一些變量來存儲數(shù)據(jù),然后查詢單個數(shù)據(jù)庫行人乓,如下所示:
type user struct {
id int
username string
password string
createdAt time.Time
}
rows, err := db.Query(`SELECT id, username, password, created_at FROM users`) // check err
defer rows.Close()
var users []user
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.username, &u.password, &u.createdAt) // check err
users = append(users, u)
}
err := rows.Err() // check err
user切片現(xiàn)在包含的內(nèi)容如下:
users {
user {
id: 1,
username: "johndoe",
password: "secret",
createdAt: time.Time{wall: 0x0, ext: 63701044325, loc: (*time.Location)(nil)},
},
user {
id: 2,
username: "alice",
password: "bob",
createdAt: time.Time{wall: 0x0, ext: 63701044622, loc: (*time.Location)(nil)},
},
}
刪除用戶
最后勤篮,從表中刪除用戶與上述各節(jié)中的.Exec一樣簡單:
_, err := db.Exec(`DELETE FROM users WHERE id = ?`, 1) // check err
代碼
package main
import (
"database/sql"
"fmt"
"log"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "root:root@(127.0.0.1:3306)/root?parseTime=true")
if err != nil {
log.Fatal(err)
}
if err := db.Ping(); err != nil {
log.Fatal(err)
}
{ // Create a new table
query := `
CREATE TABLE users (
id INT AUTO_INCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL,
created_at DATETIME,
PRIMARY KEY (id)
);`
if _, err := db.Exec(query); err != nil {
log.Fatal(err)
}
}
{ // Insert a new user
username := "johndoe"
password := "secret"
createdAt := time.Now()
result, err := db.Exec(`INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)`, username, password, createdAt)
if err != nil {
log.Fatal(err)
}
id, err := result.LastInsertId()
fmt.Println(id)
}
{ // Query a single user
var (
id int
username string
password string
createdAt time.Time
)
query := "SELECT id, username, password, created_at FROM users WHERE id = ?"
if err := db.QueryRow(query, 1).Scan(&id, &username, &password, &createdAt); err != nil {
log.Fatal(err)
}
fmt.Println(id, username, password, createdAt)
}
{ // Query all users
type user struct {
id int
username string
password string
createdAt time.Time
}
rows, err := db.Query(`SELECT id, username, password, created_at FROM users`)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var users []user
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.username, &u.password, &u.createdAt)
if err != nil {
log.Fatal(err)
}
users = append(users, u)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
fmt.Printf("%#v", users)
}
{
_, err := db.Exec(`DELETE FROM users WHERE id = ?`, 1)
if err != nil {
log.Fatal(err)
}
}
}