文档教程【免费下载链接】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点击查看免费下载本文是《build-web-application-with-golang》第 5.2 节的深入解读面向需要在 Go Web 后端中接入 MySQL 的开发者。全文以database/sql标准接口为骨架覆盖 MySQL 驱动的选型与理由、DSN 连接串的完整格式、预编译语句Prepare/Exec的增删改查CRUD实战并结合仓库内的配套示例代码main.go、schema.sql讲解底层执行原理与防 SQL 注入的机制。读完本文你将能够独立完成 Go 程序与 MySQL 的连接、建表、插入、更新、查询与删除并理解database/sql连接池与驱动注册的工作方式。MySQL 在 Go Web 架构中的位置目前 Internet 上流行的网站构架方式是 LAMP其中的 M 即 MySQL。作为数据库MySQL 以免费、开源、使用方便等优势成为了很多 Web 开发的后端数据库存储引擎。在 Go 生态中Go 官方并没有提供数据库驱动而是通过database/sql标准库定义了一组数据库驱动接口开发者只需按照这套标准接口编写驱动上层业务代码就可以在将来迁移数据库时做到最小改动。上一节 database/sql 接口 已经详细介绍了sql.Register、driver.Driver、driver.Conn、driver.Stmt、driver.Tx等底层接口本节则以 MySQL 为例展示在这套标准接口之上完成真实 CRUD 的全部步骤。Go 中可用的 MySQL 驱动Go 中支持 MySQL 的驱动目前比较多大致可以分为两类一类支持database/sql标准接口另一类采用了自己的实现接口。常用的有如下几种go-sql-driver/mysql支持database/sql全部采用 Go 编写。当前仓库各语言版本配套代码如 en/code/src/apps/ch.5.2/main.go均使用该驱动。ziutek/mymysql支持database/sql也支持自定义的接口全部采用 Go 编写。Philio/GoMySQL不支持database/sql使用自定义接口全部采用 Go 编写。本书接下来的例子主要以第一个驱动为例这也是作者实际项目中采用的驱动并推荐读者使用它主要理由如下这个驱动比较新维护得比较好完全支持database/sql接口业务代码不绑定具体驱动便于将来迁移支持 keepalive保持长连接。虽然 mikespook fork 的 mymysql 也支持 keepalive但不是线程安全的而 go-sql-driver/mysql 从底层就支持了 keepalive。需要特别说明的是go-sql-driver/mysql 属于第三方驱动需要单独安装。仓库配套示例的 readmeen/code/src/apps/ch.5.2/readme.md给出了标准安装步骤其中第 4 步就是执行go get下载并安装远程依赖包。这也是main.go中通过_ github.com/go-sql-driver/mysql匿名导入该驱动的前提。示例数据库表结构接下来的几个小节包括 使用 SQLite、PostgreSQL 等都将采用同一个数据库结构数据库test用户表userinfo关联用户信息表userdetail。建表 SQL 如下CREATE TABLE userinfo ( uid INT(10) NOT NULL AUTO_INCREMENT, username VARCHAR(64) NULL DEFAULT NULL, department VARCHAR(64) NULL DEFAULT NULL, created DATE NULL DEFAULT NULL, PRIMARY KEY (uid) ); CREATE TABLE userdetail ( uid INT(10) NOT NULL DEFAULT 0, intro TEXT NULL, profile TEXT NULL, PRIMARY KEY (uid) )仓库中的 schema.sql 与上述表结构一致uid为自增主键AUTO_INCREMENTusername、department、created分别记录用户名、所属部门与创建日期。userdetail通过uid与userinfo建立一对一关联用于扩展用户的个人介绍intro与画像profile信息。在运行示例前请先确保本机 MySQL 已安装并启动且已经创建好test数据库与访问用户。配套 readmeen/code/src/apps/ch.5.2/readme.md建议的步骤如下安装并运行 MySQL按照main.go中的常量创建用户与数据库const ( DB_USER user DB_PASSWORD DB_NAME test )执行 schema.sql 创建userinfo表执行go get下载并安装远程依赖包用go run main.go运行程序。使用 database/sql 接口对 MySQL 执行增删改查下面这个示例演示了如何使用database/sql接口对数据库表进行增删改查操作。这是原文档的核心代码也是仓库 en/code/src/apps/ch.5.2/main.go 的实现基础package main import ( database/sql fmt //time _ github.com/go-sql-driver/mysql ) func main() { db, err : sql.Open(mysql, astaxie:astaxie/test?charsetutf8) checkErr(err) //插入数据 stmt, err : db.Prepare(INSERT INTO userinfo SET username?,department?,created?) checkErr(err) res, err : stmt.Exec(astaxie, 研发部门, 2012-12-09) checkErr(err) id, err : res.LastInsertId() checkErr(err) fmt.Println(id) //更新数据 stmt, 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) //查询数据 rows, err : db.Query(SELECT * FROM userinfo) checkErr(err) for rows.Next() { var uid int var username string var department string var created string err rows.Scan(uid, username, department, created) checkErr(err) fmt.Println(uid) fmt.Println(username) fmt.Println(department) fmt.Println(created) } //删除数据 stmt, 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) db.Close() } func checkErr(err error) { if err ! nil { panic(err) } }仓库中的 main.go 在细节上做了一些工程化调整它把数据库账号、密码、库名抽成了DB_USER、DB_PASSWORD、DB_NAME三个常量用fmt.Sprintf动态拼接 DSN并在每个操作前打印Inserting、Updating、Querying、Deleting提示还用defer db.Close()替代了手动关闭。这些做法在真实项目中更易维护但其 CRUD 调用链与原文档完全一致。通过上面的代码我们可以看出Go 操作 MySQL 数据库是很方便的整个过程只依赖database/sql标准接口与具体数据库驱动解耦驱动只需在init函数中通过sql.Register完成注册详见 database/sql 接口上层代码即可直接使用。关键的几个函数解释sql.Open()用来打开一个注册过的数据库驱动。go-sql-driver/mysql 在包初始化时注册了mysql这个驱动名称所以第一个参数传入mysql第二个参数是 DSNData Source Name由 go-sql-driver 定义的数据库连接与配置信息组成。DSN 支持如下格式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各格式含义拆解如下DSN 格式连接方式说明userunix(/path/to/socket)/dbname?charsetutf8Unix 域套接字通过本地 socket 文件连接常用于本机 MySQLuser:passwordtcp(localhost:5555)/dbname?charsetutf8TCP指定主机与端口默认端口为 3306user:password/dbname默认协议不显式写协议时使用驱动默认的本地连接方式user:passwordtcp([de:ad:be:ef::ca:fe]:80)/dbnameIPv6 TCP主机为 IPv6 地址时需要加方括号参数?charsetutf8用于指定连接使用的字符集避免中文乱码。需要说明的是sql.Open只是校验参数并返回*sql.DB对象真正的网络连接会在第一次实际使用时才建立。db.Prepare()返回准备要执行的 SQL 操作然后返回准备完毕的执行状态*sql.Stmt。它对应 database/sql 接口 中driver.Stmt的Prepare概念——SQL 会被预编译后续可以反复用不同参数执行减少重复解析开销。db.Query()直接执行 SQL 并返回*sql.Rows结果集适用于 SELECT 查询。Rows对应 database/sql 接口 中的driver.Rows通过rows.Next()逐行迭代再调用rows.Scan()把当前行的列值按顺序写入传入的指针变量。stmt.Exec()执行stmt准备好的 SQL 语句适用于 INSERT、UPDATE、DELETE 等不返回结果集的操作。执行后返回sql.Result其中res.LastInsertId()返回由数据库执行插入操作得到的自增 ID 号对应 database/sql 接口 中driver.Result的LastInsertIdres.RowsAffected()返回执行 Update/Insert 等操作影响的数据条目数对应driver.Result的RowsAffected。参数占位符与 SQL 注入防护注意上面的代码中所有传入 SQL 的数据都是通过?占位符传递的INSERT ... SET username?,department?,created?、UPDATE ... SET username? WHERE uid?、DELETE ... WHERE uid?再在stmt.Exec(...)中按顺序传入对应的值。这种方式可以一定程度上防止 SQL 注入预编译语句会把参数作为数据而非 SQL 片段处理恶意输入如 OR 11不会被拼接到语句中执行。对比直接字符串拼接// 危险存在 SQL 注入风险 db.Query(SELECT * FROM userinfo WHERE username userInput )使用占位符的写法如上文示例是更安全的选择。同时?的个数必须与传入参数一一对应database/sql 接口 中提到driver.Stmt的NumInput()返回预留参数的个数当返回值 0时驱动会智能检查调用者传入的参数是否匹配。database/sql 连接池长连接是如何维持的除了驱动自身的 keepalivedatabase/sql内部还实现了一个连接池这是 MySQL 长连接得以高效复用的关键。database/sql 接口 中展示了DB结构的核心字段type DB struct { driver driver.Driver dsn string mu sync.Mutex // protects freeConn and closed freeConn []driver.Conn closed bool }其中freeConn就是那个简易的连接池。它的实现相当简单当执行db.prepare→db.prepareDC时会defer dc.releaseConn然后调用db.putConn把连接放回连接池每次调用db.conn时先判断freeConn的长度是否大于 0大于 0 说明有可以复用的连接直接拿出来用否则创建一个新连接再返回。因此即使你的业务代码反复sql.Open或频繁执行 SQL底层也会尽量复用已建立的 MySQL 连接配合 go-sql-driver/mysql 底层的 keepalive 支持长连接的稳定性更有保障。由于驱动已经按照database/sql的建议实现了连接池你不用再自行实现缓存连接否则反而容易引起问题。运行与验证在完成 MySQL 安装、建库建表之后按以下步骤运行示例# 1. 获取第三方驱动依赖 go get github.com/go-sql-driver/mysql # 2. 运行程序确保账号密码与 DSN 中的配置一致 go run main.go预期输出大致为id of last inserted row 1 1 row(s) changed uid | username | department | created 1 | astaxieupdate | 研发部门 | 2012-12-09 1 row(s) changed可以看到插入返回自增 ID更新返回受影响行数查询逐行打印出最新数据删除后同样返回受影响行数。如果账号、库名或表结构不正确程序会在checkErr处panic并打印错误信息这也是示例用统一checkErr包装所有错误调用的目的——一旦某一步失败立即暴露问题。小结与延伸阅读本节以 go-sql-driver/mysql 为例走完了 Go 使用 MySQL 的完整链路驱动选型 → 建表 → DSN 连接 → Prepare/Exec 增删改查 → 结果解析。核心要点有三使用支持database/sql的驱动如 go-sql-driver/mysql业务代码不绑定具体数据库便于未来迁移通过?占位符 stmt.Exec传入参数既高效又可防 SQL 注入database/sql内部自带简单连接池配合驱动 keepalive长连接使用更安全稳定。对比阅读 使用 SQLite 数据库 一节可以发现SQLite 示例的 CRUD 代码与本节几乎一模一样唯一改变的是导入的驱动和sql.Open的连接串——这正是database/sql标准接口一次编写、多处迁移价值的直观体现。后续还可以继续学习 使用 PostgreSQL 数据库 与 使用 Redis 等章节进一步掌握 Go 中多数据源的接入方式。相关参考第 5 章目录上一节database/sql 接口下一节使用 SQLite 数据库配套示例en/code/src/apps/ch.5.2/main.go、en/code/src/apps/ch.5.2/schema.sql、en/code/src/apps/ch.5.2/readme.md赞分享文档教程【免费下载链接】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驱动选型、DSN 配置与 CRUD 实战build-web-application-with-golang 第 5.2 节详解使用 Go database/sql 操作 MySQL驱动选型、DSN 配置与 CRUD 实战build web application with gola文档教程Go 中使用 MySQL 数据库基于 database/sql 的驱动选择与 CRUD 实战Build Web Application with Golang 第 5.2 节Go 中使用 MySQL 数据库基于 database/sql 的驱动选择与 CRUD 实战Build Web Application with Golan文档教程Build Web Application with Golang 第 5.2 节使用 Go 的 MySQL 驱动实现 database/sql 增删改查实战Build Web Application with Golang 第 5.2 节使用 Go 的 MySQL 驱动实现 database/sql 增删改查实战文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?