




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、简介Golang 提供了 database/sql 包用于对 SQL 的数据库的访问, 在这个包中, 最重要的自然就是 sql.DB 了.对于 sql.DB , 我们需要强调的是, 它并不代表一个数据库连接 , 它是一个已存在的数据库的抽象访问接口. sql.DB 为我们提供了两个重要的功能:· sql.DB 通过数据库驱动为我们管理底层数据库连接的打开和关闭操作.· sql.DB 为我们管理数据库连接池有一点需要注意的是, 正因为 sql.DB 是以连接池的方式管理数据库连接, 我们每次进行数
2、据库操作时, 都需要从连接池中取出一个连接, 当操作任务完成时, 我们需要将此连接返回到连接池中, 因此如果我们没有正确地将连接返回给连接池, 那么会造成 db.SQL 打开过多的数据库连接, 使数据库连接资源耗尽.MySQL 数据库的基本操作数据库驱动的导入有过数据库开发经验的朋友就知道了, 我们需要借助于一个数据库驱动才能和具体的数据库进行连接. 这在 Golang 中也不例外. 例如以 MySQL 数据库为例:import ( "database/sql" _ ")需要注意的是, 通常来说, 我们不应该直接使用驱动所提供的方法, 而是应该使用 sql.DB,
3、 因此在导入 mysql 驱动时, 我们使用了匿名导入的方式(在包路径前添加 _ ).当导入了一个数据库驱动后, 此驱动会自行初始化并注册自己到 Golang 的 database/sql 上下文中, 因此我们就可以通过 database/sql 包提供的方法访问数据库了.数据库的连接当导入了 MySQL 驱动后, 我们打开数据库连接:func main() db, err := sql.Open("mysql", "user:passwordtcp(127.0.0.1:3306)/test") if err != nil log.F
4、atal(err) defer db.Close()通过 sql.Open 函数, 可以创建一个数据库抽象操作接口, 如果打开成功的话, 它会返回一个 sql.DB 指针.sql.Open 函数的签名如下:func Open(driverName, dataSourceName string) (*DB, error)它接收两个参数:· driverName, 使用的驱动名. 这个名字其实就是数据库驱动注册到 database/sql 时所使用的名字.· dataSourceName, 第二个数据库连接的链接. 这个链接包含了数据库的用户名, 密码, 数据库主机以及需要连接
5、的数据库名等信息.需要注意的是, golang 对数据库的连接是延时初始化的(lazy init), 即 sql.Open 并不会立即建立一个数据库的网络连接, 也不会对数据库链接参数的合法性做检验, 它仅仅是初始化一个 sql.DB 对象. 当我们进行第一次数据库查询操作时, 此时才会真正建立网络连接.如果我们想立即检查数据库连接是否可用, 那么可以利用 sql.DB 的 Ping 方法, 例如:err = db.Ping()if err != nil log.Fatal(err)sql.DB 的最佳实践:sql.DB 对象是作为长期生存的对象来使用的, 我们应当避免频繁地调用 O
6、pen() 和 Close(). 即一般来说, 我们要对一个数据库进行操作时, 创建一个 sql.DB 并将其保存起来, 每次操作此数据库时, 传递此 sql.DB 对象即可, 最后在需要对此数据库进行访问时, 关闭对应的 sql.DB 对象.数据库的查询数据库查询的一般步骤如下:· 调用 db.Query 执行 SQL 语句, 此方法会返回一个 Rows 作为查询的结果· 通过 rows.Next() 迭代查询数据.· 通过 rows.Scan() 读取每一行的值· 调用 db.Close() 关闭查询例如我们有如下一个数据库表:CREATE TABL
7、E user ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(20) DEFAULT '', age int(11) DEFAULT '0', PRIMARY KEY (id) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4我们向其中插入一条记录:func insertData(db *sql.DB) rows, err := db.Query(INSERT INTO user (id, name, age) VALUES (1, "
8、;xys", 20) defer rows.Close() if err != nil log.Fatalf("insert data error: %vn", err) var result int rows.Scan(&result) log.Printf("insert result %vn", result)通过调用 db.Query, 我们执行了一条 INSERT 语句插入了一条数据. 当执行完毕后, 首先需要做的是检查语句是否执行成功, 当没有错误时, 就通过 rows.Scan 获取执行的结果. 因为 INSERT 返回的
9、是插入的数据的行数, 因此我们打印的语句就是 "insert result 0".接下来如法炮制, 我们从数据库中将插入的数据取出:func selectData(db *sql.DB) var id int var name string var age int rows, err := db.Query(SELECT * From user where id = 1) if err != nil log.Fatalf("insert data error: %vn", err) return for rows.Next() rows.Scan(&am
10、p;id, &age, &name) if err != nil log.Fatal(err) log.Printf("get data, id: %d, name: %s, age: %d", id, name, age) err = rows.Err() if err != nil log.Fatal(err) 上面的代码的流程基本上没有很大的差别, 不过我们需要注意一点的是 rows.Scan 参数的顺序很重要, 需要和查询的结果的 column 对应. 例如 "SELECT * From user where id =
11、1" 查询的行的 column 顺序是 "id, name, age", 因此 rows.Scan 也需要按照此顺序 rows.Scan(&id, &name, &age), 不然会造成数据读取的错位.注意 :1. 对于每个数据库操作都需要检查是否有错误返回2. 每次 db.Query 操作后, 都需要调用 rows.Close(). 因为 db.Query() 会从数据库连接池中获取一个连接, 如果我们没有调用 rows.Close(), 则此连接会一直被占用. 因此通常我们使用 defer rows.Close()&
12、#160;来确保数据库连接可以正确放回到连接池中.3. 多次调用 rows.Close() 不会有副作用, 因此即使我们已经显示地调用了 rows.Close(), 我们还是应该使用 defer rows.Close() 来关闭查询.完整的例子如下:func insertData(db *sql.DB) rows, err := db.Query(INSERT INTO user (id, name, age) VALUES (1, "xys", 20) defer rows.Close() if err != nil log.Fatalf("
13、insert data error: %vn", err) var result int rows.Scan(&result) log.Printf("insert result %vn", result)func selectData(db *sql.DB) var id int var name string var age int rows, err := db.Query(SELECT id, name, age From user where id = 1) if err != nil log.Fatalf("insert data e
14、rror: %vn", err) return for rows.Next() err = rows.Scan(&id, &name, &age) if err != nil log.Fatal(err) log.Printf("get data, id: %d, name: %s, age: %d", id, name, age) err = rows.Err() if err != nil log.Fatal(err) func main() db, err := sql.Open("mysql", "ro
15、ot:roottcp(127.0.0.1:3306)/test") defer db.Close() if err != nil fmt.Printf("connect to db 127.0.0.1:3306 error: %vn", err) return insertData(db) selectData(db)预编译语句(Prepared Statement)预编译语句(PreparedStatement)提供了诸多好处, 因此我们在开发中尽量使用它. 下面列出了使用预编译语句所提供的功能:· PreparedStatement 可以实现自定义参
16、数的查询· PreparedStatement 通常来说, 比手动拼接字符串 SQL 语句高效.· PreparedStatement 可以防止SQL注入攻击下面我们将上一小节的例子使用 Prepared Statement 来改写:func deleteData(db *sql.DB) stmt, _ := db.Prepare(DELETE FROM user WHERE id = ?) rows, err := stmt.Query(1) defer stmt.Close() rows.Close() if err != nil log.Fatalf("de
17、lete data error: %vn", err) rows, err = stmt.Query(2) rows.Close() if err != nil log.Fatalf("delete data error: %vn", err) func insertData(db *sql.DB) stmt, _ := db.Prepare(INSERT INTO user (id, name, age) VALUES (?, ?, ?) rows, err := stmt.Query(1, "xys", 20) defer stmt.Clo
18、se() rows.Close() if err != nil log.Fatalf("insert data error: %vn", err) rows, err = stmt.Query(2, "test", 19) var result int rows.Scan(&result) log.Printf("insert result %vn", result) rows.Close()func selectData(db *sql.DB) var id int var name string var age int stmt, _ := db.Prepare(SELECT * From user where age >
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 科学助力儿童健康成长公益行动
- 科室护理质控总结效果评价
- 人工全髋关节置换护理
- 内科诊疗体系概述
- 儿童睡眠健康宣教指南
- 渗滤液安全培训
- 户型设计核心方案解析
- 消化道异物护理
- 2025年泡腾剂项目提案报告
- 2025年硅酮结构密封胶项目立项申请报告
- 【人教版】河北石家庄2024-2025学年 四年级下学期期末数学试题【一】有解析
- 2025-2030年中国下一代测序(NGS)数据分析行业市场现状供需分析及投资评估规划分析研究报告
- 智能安全帽在智慧工地中的应用与管理平台研究
- Machine-Cmk-设备能力指数Cmk分析表
- DB65∕T 3952-2016 反恐怖防范设置规范 学校
- 科研助理合同协议书
- 离散余弦变换教学课件
- 术后镇痛考试试题及答案
- 《诸子百家教学课件》课件
- 2025+CSCO胃癌诊疗指南解读
- 药学三基培训课件
评论
0/150
提交评论