今天我们继续接着前几篇关于 Goweb 编程的文章往下延伸. 在 Web 应用程序中几乎每个应用场景都需要存储和检索数据库中的数据. 当你处理动态内容, 为用户提供表单以输入数据或存储登录名和密码凭据以供用户进行身份验证时, 都需要用到数据库. MySQL 数据库是整个互联网中最常用的数据库. MySQL 已经存在了很长时间, 还在不停的进化并且随着互联网一起发展已多次证明了其位置和稳定性.
本文我们将探究 Go 中数据库访问的基础知识, 在开始之前我们先更新一下我们使用的开发环境, 之前在文章 用 Docker 快速搭建 Go 开发环境 中我们只应用了一个运行 go 的容器, 现在我们为开发环境加上数据库.
在开发环境中增加 MySQL 容器
打开我们之前编写的 docker-compose.YAML 文件, 添加如下配置:
因为容器退出时会销毁容器内的所有文件, 所以对于 MySQL 这种存储持久化数据的容器需要与外部宿主机做文件映射, 这样再次启动 MySQL 容器后就会从数据映射中读取之前的数据.
在编排文件的中我们通过 volumes 命令创建了一个名为 dbdata 的数据卷( dbdata 后面的冒号是有意写上去的, 这是 YAML 文件的一个语法限制, 不用太关心)
定义完数据卷后, 在上面我们使用 dbdata:/var/lib/MySQL 的格式, 通知 Docker, 将 dbdata 数据卷挂在到容器中的 /var/lib/MySQL 目录上.
environments 中设置的是 MySQL 容器需要的四个必要参数.
ports 端口映射中, 我们将本地电脑的 33063 端口映射到容器的 3306 端口, 这样我们就能通过电脑上的数据库工具连接到 容器内的 MySQL 了.
添加完 MySQL 后完整的编排文件就变成:
可以公众号内发送 gohttp04 获取 Docker 编排文件和文章中用到的源代码.
安装 go-sql-driver/MySQL 包
Go 语言标准库中 database/sql 包, 用于查询各种 SQL 数据库. 它将所有通用 SQL 功能抽象到一个 API 中供开发者使用. 但是 Go 的标准库中不包括数据库驱动程序. 数据库驱动程序由特定软件包提供的, 用于实现特定数据库底层的封装. 这对于向前兼容很有用, 也使得 Go 不会变得臃肿. 因为在创建所有 Go 软件包时, 开发人员无法预见未来会有什么数据库会被投入使用, 而且要支持每个可能的数据库将需要进行大量维护工作.
使用下面命令安装 MySQL 驱动包:
连接 MySQL 数据库
要检查我们是否可以连接到数据库, 我们需要导入 database/sql 和 go-sql-driver/MySQL 两个包, 并连接数据库:
你的代码应仅引用在 database/sql 中定义的类型和函数. 这有助于避免使代码依赖于特定驱动程序, 从而使你可以通过最少的代码更改来更改使用的数据库驱动(相应也会更改使用的数据库类型).
代码中对驱动包使用匿名包导入, 将 go-sql-driver/MySQL 的包别名设置为 _ , 这样驱动程序导出的名称对我们的代码都是不可见的, 但是在幕后 go-sql-driver/MySQL 将自身注册为可用于 database/sql 的驱动包. 一般而言, 除了运行包的 init 函数外, 不会发生任何其他事情.
sql.Open() 不会建立与数据库的任何连接, 也不会验证驱动程序的连接参数. 它只是返回抽象数据库的对象以供后面使用. 数据库连接在真正需要访问数据库的时候才会建立.
我们可以通过单元测试验证数据库是否能正确连接上, 测试代码我就不贴了, 可以通过文章的源码包里看到, 唯一提醒一点, 如果在本地机器里运行测试需要把上面 sql.Open() 配置的端口改为 33063
创建表
我们接下来创建一个像这样的表:
id | username | password | created_at |
---|---|---|---|
1 | Joshua | secret | 2020-02-13 12:30:00 |
使用 database/sql 包执行建表语句就可以在我们的 MySQL 数据库中创建表:
插入新数据
默认情况下, Go 使用准备好的语句 (prepare) 将动态数据插入到我们的 SQL 语句中, 这是一种将用户提供的数据安全地传递到我们的数据库而不会造成任何损坏的方式. 在 Web 编程的早期, 程序员将数据和查询直接传递给数据库, 这导致了巨大的漏洞, 并可能破坏整个 Web 应用程序.
要将我们的第一个用户插入数据库表, 我们将创建一个如下的 SQL 查询. 语句中的问号告诉 SQL 驱动程序, 它们是实际数据的占位符. 下面你可以看到我们讨论的准备好的语句:
结果包含最后插入的 ID(自增 ID)的信息以及此查询影响的行数.
查询表数据
现在我们的表中有一个用户, 我们想要查询它并获取其所有信息. 使用 database/sql 包我们有两种查询表的方式. db.Query 可以查询多行, 以便我们进行迭代; db.QueryRow 查询特定的行.
查询单行
我们首先声明一些变量来存储数据, 然后查询单个数据库行:
查询多行
上面我们演示了如何查询单个用户行, 接下来演示下如何查询多个数据行并将数据存储到结构体切片中:
篇幅原因代码中所有的错误检查都被故意忽略了, 在实际使用中一定要记得做错误检查.
users 切片中存储的数据类似这样:
删除表数据
从我们的表中删除数据同创建表和插入数据一样也是使用 .Exec :
后续
database/sql 提供的 MySQL 查询功能还是很全面的, 更多介绍可以参考 http://go-database-sql.org/index.html, 不过它是一个相对偏底层的库. 实际开发中往往会使用一些在它的基础上封装的 ORM 库. ORM 的查询使用起来更简单些, 语法表达力更强也更方便于代码管理. 所以今天的文章主要是对 database/sql 做一下简单介绍, 入门即可, 后续关于 ORM 库的使用时再介绍更多查询的使用方法. 另外今天在我们的 Docker 环境中增加了 MySQL 容器, 大家也不要忘记更新.
公众号回复 gohttp04 获取更新后的 Docker 编排文件和文章中用到的源代码, 欢迎大家踊跃留言交流想法. 喜欢我文章的朋友帮忙转发和点在看支持, 谢谢大家的关注.
前文回顾
深入学习用 Go 编写 HTTP 服务器
Web 服务器路由
用 Docker 快速搭建 Go 开发环境
十分钟学会用 Go 编写 Web 中间件
来源: http://www.tuicool.com/articles/jeI3E3U