ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

build-web-application-with-golang 第 5.2 章实战:用 Go database/sql 驱动 MySQL 完成增删改查

build-web-application-with-golang 第 5.2 章实战:用 Go database/sql 驱动 MySQL 完成增删改查 文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本章实战围绕仓库中 en/code/src/apps/ch.5.2 目录下的可运行示例展开。该示例与 《Build Web Application with Golang》第 5.2 章 MySQL 一一对应演示了如何基于 Go 标准库database/sql接口 github.com/go-sql-driver/mysql纯 Go 驱动对 MySQL 执行插入、更新、查询、删除四类核心操作。读完本文你将掌握这套示例从「安装 MySQL → 建库建表 → 拉取依赖 → 编译运行」的完整落地流程并能读懂database/sql中sql.Open、db.Prepare、stmt.Exec、rows.Scan等关键 API 的底层行为与正确用法。一、示例在全书中的定位第 5 章讨论的是 Go 的数据库访问层。在 5.1 节 database/sql interface 中作者详细剖析了 Go 标准库database/sql与database/sql/driver的接口体系驱动通过sql.Register(name, driver)在init()中注册自己driver.Driver、driver.Conn、driver.Stmt、driver.Tx、driver.Result、driver.Rows等接口共同构成了驱动与上层代码的解耦层。正因为上层代码只依赖database/sql标准接口5.2、5.3、5.4、5.5 各节可以用完全相同的调用模式去对接 MySQL、SQLite、PostgreSQL 等不同数据库——这正是本示例值得动手跑一遍的原因它是验证「接口标准化 驱动可插拔」这一设计的最佳最小实验。本示例的目录 en/code/src/apps/ch.5.2 由三个文件组成文件作用main.go完整的 CRUD 示例程序可编译、可运行schema.sqluserinfo表的建表语句readme.md运行本示例的 5 步设置指南二、运行准备5 步设置指南逐条拆解readme.md 给出的设置流程非常精炼下面结合仓库实际文件逐条展开保证任何环境都能照做。Step 1安装并启动 MySQL示例程序通过 TCP 连接 MySQL 服务端。在 Debian/Ubuntu 系可直接sudo apt-get install mysql-server sudo service mysql startmacOS 可用 Homebrewbrew install mysql brew services start mysql安装完成后可用mysql -u root -p验证服务已就绪。示例本身对 MySQL 版本没有特殊要求标准 5.x 与 8.x 均可。Step 2按 main.go 中的常量创建用户与数据库示例把连接所需的账号信息直接写成常量见 main.goconst ( DB_USER user DB_PASSWORD DB_NAME test )这三个常量对应关系如下常量值含义DB_USERuserMySQL 登录用户名DB_PASSWORD空字符串密码示例默认无密码DB_NAMEtest目标数据库名也就是说你需要保证 MySQL 中存在一个用户名为user、密码为空的账号并且该账号对名为test的数据库有读写权限。若你的环境与之不同直接修改这三个常量即可无需改动任何业务代码——这是该示例刻意把「环境差异」收敛到常量层的设计。以 root 身份执行CREATE DATABASE test; CREATE USER userlocalhost IDENTIFIED BY ; GRANT ALL PRIVILEGES ON test.* TO userlocalhost; FLUSH PRIVILEGES;注意示例 DSN 中未携带密码user/test形式因此密码为空是满足运行条件的最简配置。生产环境请务必使用强密码不要把凭证硬编码进源码。Step 3用 schema.sql 创建 userinfo 表在 MySQL 客户端中执行建表脚本mysql -u user test schema.sql或进入交互模式后. source执行。建表语句位于 schema.sqlCREATE TABLE userinfo ( uid INT(10) NOT NULL AUTO_INCREMENT, username VARCHAR(64) NULL DEFAULT NULL, departname VARCHAR(64) NULL DEFAULT NULL, created DATE NULL DEFAULT NULL, PRIMARY KEY (uid) );表结构要点uid主键AUTO_INCREMENT自增插入时无需显式赋值程序通过LastInsertId()取回新行 IDusername/departname长度 64 的字符串列createdDATE类型示例插入的是字符串2012-12-09MySQL 会按日期格式自动转换。一个小提醒示例代码 main.go 与 schema.sql 使用的是departname列名而 5.2 章节正文 中的建表 SQL 写作department二者并不一致。以仓库内可运行的 schema.sql 为准二者择一保持一致即可切勿混用导致 SQL 报错。Step 4运行 go get 安装远程依赖示例只依赖一个第三方包MySQL 驱动github.com/go-sql-driver/mysql。在项目目录下执行go get github.com/go-sql-driver/mysqlgo get会把驱动源码下载到本地并可供编译链接。需要说明的是仓库并未为示例提供go.mod或vendor目录依赖管理方式取决于你使用的 Go 版本使用 Go ModulesGo 1.11 默认建议先go mod init ch5.2再执行go get让go mod tidy生成go.mod/go.sum使用传统GOPATH模式可参考仓库根下 en/code/readme.md 的建议把GOPATH指向代码目录从而任意目录下都能正常 import。Step 5运行程序一切就绪后在 ch.5.2 目录 下执行go run main.go程序会依次打印插入、更新、查询、删除四阶段的运行日志具体输出含义见下一节源码解读。三、main.go 源码逐段解读main.go 完整演示了database/sql的标准用法下面按执行顺序拆解。3.1 导入与驱动注册import ( database/sql fmt _ github.com/go-sql-driver/mysql )注意_ github.com/go-sql-driver/mysql的空导入写法如 5.1 节 所讲Go 禁止导入后不使用的包而空标识符导入的作用是只执行该包的init()而不直接引用其符号——go-sql-driver 正是利用init()调用sql.Register(mysql, ...)完成驱动注册。注册后sql.Open(mysql, dsn)的第一个参数才能匹配到该驱动。3.2 构造 DSN 并打开数据库句柄dbSouce : fmt.Sprintf(%v:%v/%v?charsetutf8, DB_USER, DB_PASSWORD, DB_NAME) db, err : sql.Open(mysql, dbSouce) checkErr(err) defer db.Close()按前面常量展开实际 DSN 为user:/test?charsetutf85.2 章节正文 给出了database/sql驱动的 DSN 支持格式全集除本示例使用的user:password/dbname外还包括userunix(/path/to/socket)/dbname?charsetutf8 user:passwordtcp(localhost:5555)/dbname?charsetutf8 user:password/dbname user:passwordtcp([de:ad:be:ef::ca:fe]:80)/dbname可见之前是认证信息之后可省略tcp(host:port)而走默认本地连接也可显式指定 TCP 地址、Unix Socket 甚至 IPv6 地址?charsetutf8是连接参数保证客户端与 MySQL 之间的字符集一致。需要特别澄清sql.Open的语义它并不立即建立物理连接只是初始化一个带连接池的DB句柄并校验 DSN 格式真正的连接在首次执行 SQL 时才建立。db.Close()负责归还并关闭池中连接。从源码结构看这正是 5.1 节 中database/sql包在DB结构上维护freeConn连接池defer db.putConn(ci, err)回收连接、复用空闲连接的高层封装。3.3 插入Prepare Exec LastInsertIdstmt, err : db.Prepare(INSERT userinfo SET username?,departname?,created?) checkErr(err) res, err : stmt.Exec(astaxie, software developement, 2012-12-09) checkErr(err) id, err : res.LastInsertId() checkErr(err) fmt.Println(id of last inserted row , id)调用链为db.Prepare让数据库预先编译 SQL 得到Stmtstmt.Exec传入三个占位符参数执行插入res.LastInsertId()取回自增主键uid的值供后续 update/delete 使用。?占位符由驱动在客户端做参数化绑定避免字符串拼接注入风险详见本文第四节。3.4 更新Exec RowsAffectedstmt, err db.Prepare(update userinfo set username? where uid?) checkErr(err) res, err stmt.Exec(astaxieupdate, id) checkErr(err) affect, err : res.RowsAffected() checkErr(err) fmt.Println(affect, row(s) changed)注意这里复用了同一个stmt变量重新赋值where uid?中的?绑定到上一步取回的id。RowsAffected()返回本次 UPDATE 实际影响的行数程序据此打印1 row(s) changed。3.5 查询Query rows.Next/Scanrows, err : db.Query(SELECT * FROM userinfo) checkErr(err) for rows.Next() { var uid int var username, department, created string err rows.Scan(uid, username, department, created) checkErr(err) fmt.Println(uid | username | department | created) fmt.Printf(%3v | %6v | %6v | %6v\n, uid, username, department, created) }与前面「先 Prepare 再 Exec」不同db.Query直接执行 SELECT 并返回*sql.Rows结果集。惯用模式是rows.Next()在循环中逐行推进每次调用rows.Scan(uid, username, department, created)把当前行的各列按顺序扫描进对应变量地址。示例中局部变量名department与表列departname略有出入但 Scan 是按列顺序取值的与变量名无关。按 5.1 节 对driver.Rows.Next的说明行数据在驱动层会被转换为[]byte等driver.Value类型再由Scan转换并填充到目标变量。3.6 删除Prepare Exec RowsAffectedstmt, err db.Prepare(delete from userinfo where uid?) checkErr(err) res, err stmt.Exec(id) checkErr(err) affect, err res.RowsAffected() checkErr(err) fmt.Println(affect, row(s) changed)按插入时拿到的id精准删除该行整个示例形成「插入 → 更新 → 查询 → 删除」的完整闭环。程序结束后defer db.Close()自动回收连接。3.7 错误处理checkErrfunc checkErr(err error) { if err ! nil { panic(err) } }示例以panic快速暴露任何一步的错误连接失败、SQL 语法错误、驱动缺失等适合教程演示场景真实业务中应改为返回 error 或记录日志而不是让程序崩溃。四、为什么必须用?占位符5.2 章节正文 特别强调传参必须使用?占位符格式这是防止 SQL 注入攻击的必要手段。示例中所有用户可控或动态生成的值用户名、ID 等都通过stmt.Exec(..., id)绑定而不是拼进 SQL 字符串。驱动会在协议层对参数做转义与类型处理恶意输入即使包含引号、分号也无法改变 SQL 结构。这是使用database/sql接口时的硬性安全规范直接照搬本示例的写法即可获得正确且安全的默认行为。五、预期运行输出在环境正确的前提下运行go run main.go输出大致为Inserting id of last inserted row 1 Updating 1 row(s) changed Querying uid | username | department | created 1 | astaxieupdate | software developement | 2012-12-09 Deleting 1 row(s) changed各阶段含义插入返回新行自增 ID更新报告影响 1 行查询回显表中全部记录此时应看到更新后的用户名删除再次报告影响 1 行。若某一步panic请按错误信息回查驱动是否go get、MySQL 是否启动、user/test库是否创建、schema.sql 是否执行。六、延伸同一套代码跑多种数据库本示例的骨架Open → Prepare/Exec → Query/Scan完全建立在database/sql标准接口之上因此只需更换驱动与 DSN 即可移植到其他数据库。仓库中同章节的姊妹示例正好提供了对照ch.5.3SQLite使用sqlite3 foo.db建库、.read schema.sql建表同样go get后go run main.goch.5.4 / ch.5.5 目录PostgreSQL、Redis沿用同构流程章节正文可参见 5.2 节MySQL 驱动选型与 DSN 格式与 5.1 节database/sql接口体系与连接池原理。对比这些示例即可直观感受到「接口标准化、驱动可插拔」带来的迁移成本优势——业务代码几乎零改动。七、小结与常见问题速查本文从 readme.md 的 5 步设置出发完整展开了 MySQL 示例的运行流程与 main.go 的 CRUD 实现并对照 schema.sql 与 5.1 节、5.2 节 章节正文讲解了sql.Open、Prepare/Exec、RowsAffected、LastInsertId、Query/Scan等 API 的语义与底层行为。最后给出高频问题的快速排查表现象常见原因panic: dial tcp ... connection refusedMySQL 未启动或 DSN 未指定tcp(host:port)且服务不在默认 3306panic: Access denied for user useruser账号未创建、密码不符或未授权test库panic: driver: bad connection驱动未通过go get安装空导入注册失败Error 1146: Table test.userinfo doesnt exist未执行 schema.sqlSQL 报列名不存在章节正文用department、示例代码用departname请与 schema.sql 保持一致按本文步骤走完一遍你就拥有了一个可直接复用的 Go MySQL CRUD 最小范例可在此基础上平滑扩展为 Web 应用的数据库访问层。赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐使用 Go 的 database/sql 与 MySQL 驱动完成增删改查build-web-application-with-golang 第 5.2 章实战使用 Go 的 database/sql 与 MySQL 驱动完成增删改查build web application with golang 第 5.2 章实文档教程Build Web Application with Golang 第 5.2 节使用 Go 的 MySQL 驱动实现 database/sql 增删改查实战Build Web Application with Golang 第 5.2 节使用 Go 的 MySQL 驱动实现 database/sql 增删改查实战文档教程Go 操作 MySQL 实战基于 database/sql 的增删改查完整指南Build Web Application with Golang 第 5.2 节Go 操作 MySQL 实战基于 database/sql 的增删改查完整指南Build Web Application with Golang 第 5.2文档教程上一篇NumPy 1.6.1 缺陷修复版本解析einsum、nditer、f2py 与对象数组回归问题的源码级解读下一篇RocketMQ 生产者启动流程源码深度解析基于 4.9.3 版本创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表