前面学院君给大家介绍了 Go 语言中的内存存储和文件存储,文件存储的好处是可以持久化数据,但是并不是 Web 应用数据存储的终极方案,因为这样存储起来的数据检索和管理起来非常麻烦,为此又诞生了数据库管理系统来处理数据的增删改查。数据库又可以划分为关系型数据库(RDBMS)和非关系型数据库(NoSQL),前者比如 MySQL、Oracle,后者比如 Redis、MongoDB,这里我们以当前最流行的开源关系型数据库 MySQL 为例进行介绍。
1、初始化数据库
开始之前,我们先要连接到 MySQL 服务器初始化数据库和数据表。
注:如果你还没有在本地安装 MySQL 数据库,需要先进行安装,使用 Docker 启动或者去 MySQL 官网下载安装包安装均可,Mac 系统中还可以使用 Homebrew 进行安装,然后选择一个自己喜欢的 GUI 客户端,学院君本地使用的是 TablePlus。
test_dbpostsCREATE TABLE `posts` (
`id` bigint unsigned AUTO_INCREMENT,
`title` varchar(100) DEFAULT NULL,
`content` text,
`author` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
2、建立数据库连接
postsdatabase/sqldatabase/sqlgo-sql-driver/mysqldb.goinitmainimport (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
var Db *sql.DB
func init() {
var err error
Db, err = sql.Open("mysql", "root:root@/test_db?charset=utf8mb4&parseTime=true")
if err != nil {
panic(err)
}
}
sql.DBsql.Openmysql[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
addresssql.DBOpensql.DBsql.DBsql.DBDbsql.DBOpensql.Registergo-sql-driver/mysqlmysqlinitdriver.gofunc init() {
sql.Register("mysql", &MySQLDriver{})
}
sqldriver.Drivermysql_ "github.com/go-sql-driver/mysql"
mysqlinitsql.Driver_database/sql注:如果你对这一块接口与实现的细节不清楚,可以回顾 Go 入门教程中的面向对象编程部分。
3、增删改查示例代码
数据库初始化完成并设置好连接配置之后,就可以在 Go 应用中与数据库进行交互了。我们将编写一段对文章表进行增删改查的示例代码来演示 Go 语言中的数据库操作。
db.go定义 Post 结构体
首先我们需要定义一个表示文章表数据结构的结构体:
type Post struct {
Id int
Title string
Content string
Author string
}
创建新文章
CreateDbfunc (post *Post) Create() (err error) {
sql := "insert into posts (title, content, author) values (?, ?, ?)"
stmt, err := Db.Prepare(sql)
if err != nil {
panic(err)
}
defer stmt.Close()
res, err := stmt.Exec(post.Title, post.Content, post.Author)
if err != nil {
panic(err)
}
postId, _ := res.LastInsertId()
post.Id = int(postId)
return
}
注:这里我们使用了预处理语句,以避免 SQL 注入攻击,如果你有 PHP 或者其他语言数据库编程基础的话,应该很容易看懂这些代码。
stmt.QueryRow(post.Title, post.Content, post.Author)Db.Execres, err := Db.Exec(sql, post.Title, post.Content, post.Author)
获取单篇文章
Db.QueryRowPostfunc GetPost(id int) (post Post, err error) {
post = Post{}
err = Db.QueryRow("select id, title, content, author from posts where id = ?", id).
Scan(&post.Id, &post.Title, &post.Content, &post.Author)
return
}
获取文章列表
sql.DBQueryfunc Posts(limit int) (posts []Post, err error) {
rows, err := Db.Query("select id, title, content, author from posts limit ?", limit)
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
post := Post{}
err = rows.Scan(&post.Id, &post.Title, &post.Content, &post.Author)
if err != nil {
panic(err)
}
posts = append(posts, post)
}
return
}
sql.RowsNextsql.Rowsql.Rowsio.EOFsql.RowPostPostposts其实对于单条记录,也可以使用类似的方式实现,毕竟单条记录查询是 SELECT 查询的特例:
func GetPost(id int) (post Post, err error) {
rows, err := Db.Query("select id, title, content, author from posts where id = ? limit 1", id)
if err != nil {
panic(err)
}
defer rows.Close()
for rows.Next() {
post = Post{}
err = rows.Scan(&post.Id, &post.Title, &post.Content, &post.Author)
if err != nil {
panic(err)
}
}
return
}
Db.Querystmt, err := Db.Prepare("select id, title, content, author from posts limit ?")
if err != nil {
panic(err)
}
defer stmt.Close()
rows, err := stmt.Query(limit)
if err != nil {
panic(err)
}
... // 后续其他操作代码
更新文章
对于已存在的文章记录,可以通过执行 SQL 更新语句进行修改:
func (post *Post) Update() (err error) {
stmt, err := Db.Prepare("update posts set title = ?, content = ?, author = ? where id = ?")
if err != nil {
return
}
stmt.Exec(post.Title, post.Content, post.Author, post.Id)
return
}
stmt.QueryRowDb.ExecDb.Execfunc (post *Post) Update() (err error) {
_, err = Db.Exec("update posts set title = ?, content = ?, author = ? where id = ?",
post.Title, post.Content, post.Author, post.Id)
return
}
Db.Execsql.Result-w632
Result_删除文章
删除操作和更新操作类似,只是将 UPDATE 语句调整为 DELETE 语句而已:
func (post *Post) Delete() (err error) {
stmt, err := Db.Prepare("delete from posts where id = ?")
if err != nil {
return
}
stmt.Exec(post.Id)
return
}
stmt.QueryRowDb.Execfunc (post *Post) Delete() (err error) {
_, err = Db.Exec("delete from posts where id = ?", post.Id)
return
}
4、整体测试
db.gomainfunc main() {
post := Post{Title: "Go 语言数据库操作", Content: "基于第三方 go-sql-driver/mysql 包实现 MySQL 数据库增删改查", Author: "学院君"}
// 创建记录
post.Create()
fmt.Println(post)
// 获取单条记录
dbPost, _ := GetPost(post.Id)
fmt.Println(dbPost)
// 更新记录
dbPost.Title = "Golang 数据库操作"
dbPost.Update()
// 获取文章列表
posts, _ := Posts(1)
fmt.Println(posts)
// 删除记录
dbPost.Delete()
}
go-sql-driver/mysqldb.go好了,关于数据库增删改查基本操作就简单介绍到这里,下篇教程,我们来看看如何在 MySQL 数据库中实现不同表之间的关联查询和更新。
(全文完)