MySQL-数据库
开始
在某些时刻,你想要你的 web 应用去存储和从数据库检索数据。经常做的一点是当你处理动态内容,用户输入表单提供的数据或者存储登录和验证用户密码凭证。 从这个目的,我们需要数据库。
有各式各样的数据库。一般所有网页使用的是 MySQL 数据。它已经存在许久和占据一定的地位和比你认知都稳定。
在这个例子,我们将会在 Go 中使用数据的基本功能,创建数据表,存储数据和检索返回数据。
安装 go-sql-driver/mysql
包
Go 语言程序有一个方便使用的包叫 database/sql
去查询所有 SQL 数据库。这是非常有用的因为它抽象SQL公有到单个 API 来提供使用。 为什么 Go 不包含一个数据库驱动。在Go, 数据库驱动是一个包用来继承特殊数据库(在我们MySQL 例子)的底层信息。你可能已经猜到,这个有效的向前兼容。之前,所有 Go 包在创建时间,作者不能预见每个单独数据库在将来的存续时间和在未来支持每种可能数据库将会是一个巨大的维护工作量。
安装 MySQL 数据库驱动,选择你的命令行打开和运行:
go get -u github.com/go-sql-driver/mysql
连接一个 MySQL 数据库
第一件事我们安装所有必要的包之后需要检查,如果我们能够连接我们 MySQL 数据库成功。如果你还没有已经运行的 MySQL 数据库服务, 你用 Docker 很容易启动一个新实例。这里是官方文档 Docker MySQL image: hub.docker.com/_/mysql
检查我们如果能够连接我们数据库, import database/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()
我们创建第一个数据库表
我们数据库的所有数据条目是存储在一个特殊表。一个数据库表由列和行组成。列给每个数据条目一个标签和指定类型给它。行是一个插入数据值。我们第一个例子中创建一个表像这样:
id | username | password | created_at |
---|---|---|---|
1 | johndoe | secret | 2019-08-10 12:30:00 |
翻译成 SQL, 创建表命令可能看起来像这样:
CREATE TABLE users (
id INT AUTO_INCREMENT,
username TEXT NOT NULL,
password TEXT NOT NULL,
created_at DATETIME,
PRIMARY KEY (id)
);
现在我们有自己的 SQL 命令,我们能使用 database/sql
包在我们 MySQL 数据库中创建表。
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, 将新数据插入表中与创建表是一样简单。需要注意的一点是:默认情况下,预处理语句将动态数据插入到 SQL 查询中,这是一种将用户提供的数据安全的传递到数据库不会造成损坏和风险的做法。在web编程的早期,程序员通过数据直接传到数据库,这导致了大量漏洞,并可能破坏整个web应用程序。请不要那样做。这样做是十分正确的。
将第一个用户插入数据库表,我们创建了 SQL 查询,如下所示。如您所见,我们省略了id列,因为它是由MySQL自动设置的. ? 告诉SQL驱动程序,它们是实际数据的占位符。这是您可以看到预处理语句是声明的地方。
INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)
我们现在能使用这条 SQL 语句和向我们表中插入新的一行。
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)
要获取用户新创建的id,只需按如下方式获取:
userID, err := result.LastInsertId()
查询 users 表
现在我们的表中有一个用户,我们想查询它并获取它的所有信息。在Go中,我们有两种查询表的可能性。
使用 db.Query
查询多行,供我们迭代和我们使用 db.QueryRow
只想查询特定行。
查询特定行的工作方式基本上与我们之前讨论过的所有其他SQL命令相同。
我们通过ID查询单个用户的 SQL 命令如下所示:
SELECT id, username, password, created_at FROM users WHERE id = ?
在Go中,我们首先声明一些变量来存储数据,然后查询单个数据库行,如下所示:
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)
查询 users 所有数据
在前面的小节中,我们介绍了如何查询单个用户行。许多应用程序想要查询所有存在的用户实例。这与上面的示例类似,但需要更多的编码。
SELECT id, username, password, created_at FROM users
我们可以使用上面示例中的SQL命令并删除 WHERE
子句。这样,我们可以查询所有现有用户。
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
用户切片现在可能包含以下内容:
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)},
},
}
从表中删除一个 user
最后,从我们的表中删除一个用户就像上面章节中的.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)
}
}
}