一, 打开 MySQL 数据库
1 下载并导入数据库驱动包
我们选择了 Go-MySQL-Driver 这个实现. 地址是: https://github.com/go-sql-driver/mysql/ .
下载驱动包:
$ go get GitHub.com/go-sql-driver/MySQL
2 导入包:
通常来说, 不应该直接使用驱动所提供的方法, 而是应该使用 sql.DB, 因此在导入 MySQL 驱动时, 这里使用了匿名导入的方式(在包路径前添加 _), 当导入了一个数据库驱动后, 此驱动会自行初始化并注册自己到 Golang 的 database/sql 上下文中, 因此我们就可以通过 database/sql 包提供的方法访问数据库了.
- import "database/sql"
- import _"github.com/go-sql-driver/mysql"
3 连接数据库
dataSourceName 格式: 用户名: 密码 @数据库名称? 编码方式
db, err := sql.Open("mysql", "root:12345678@/mydatabase?charset=utf8") if err != nil { fmt.Println(err) } defer db.Close()
二, 内部实现分析
1, 打开数据库
sql.Open("mysql", "username:pwd@/databasename")
通过调用 sql.Open 函数返回一个 sql.DB 指针; sql.Open 函数原型如下:
func Open(driverName, dataSourceName string) (*DB, error)
• driverName: 使用的驱动名. 这个名字其实就是数据库驱动注册到 database/sql 时所使用的名字.
• dataSourceName: 数据库连接信息, 这个连接包含了数据库的用户名, 密码, 数据库主机以及需要连接的数据库名等信息.
功能: 返回一个 DB 对象, DB 对象对于多个 goroutines 并发使用是安全的, DB 对象内部封装了连接池.
实现: open 函数并没有创建连接, 它只是验证参数是否合法. 然后开启一个单独 goroutines 去监听是否需要建立新的连接, 当有请求建立新连接时就创建新连接.
注意: open 函数应该被调用一次, 通常是没必要 close 的.
2,DB.Exec()
功能: 执行不返回行 (row) 的查询(这里可以理解为查询), 比如 INSERT,UPDATE,DELETE 实现: DB 交给内部的 exec 方法负责查询. exec 会首先调用 DB 内部的 conn 方法从连接池里面获得一个连接. 然后检查内部的 driver.Conn 实现了 Execer 接口没有, 如果实现了该接口, 会调用 Execer 接口的 Exec 方法执行查询; 否则调用 Conn 接口的 Prepare 方法负责查询.
3,DB.Query()
功能: 用于检索(retrieval), 比如 SELECT 实现: DB 交给内部的 query 方法负责查询. query 首先调用 DB 内部的 conn 方法从连接池里面获得一个连接, 然后调用内部的 queryConn 方法负责查询.
4,DB.QueryRow()
功能: 用于返回单行的查询.
实现: 转交给 DB.Query()查询.
5,db.Prepare()
功能: 返回一个 Stmt.Stmt 对象可以执行 Exec,Query,QueryRow 等操作.
实现: DB 交给内部的 prepare 方法负责查询. prepare 首先调用 DB 内部的 conn 方法从连接池里面获得一个连接, 然后调用 driverConn 的 prepareLocked 方法负责查询.
6, 事务
功能: 开启事务 (db.Begin()), 返回 Tx 对象. 调用该方法后, 这个 TX 就和指定的连接绑定在一起了. 一旦事务提交(tx.Commit()) 或者回滚(tx.Rollback()), 该事务绑定的连接就还给 DB 的连接池.
实现: DB 交给内部的 begin 方法负责处理. begin 首先调用 DB 内部的 conn 方法从连接池里面获得一个连接, 然后调用 Conn 接口的 Begin 方法获得一个 TX.
三, 实例分析
1, 先建立一个数据表
[图片上传中...(image.PNG-47e1a6-1545398854188-0)]
2, 代码分析
- // 打开数据库, 格式: 用户名, 密码 @数据库名称? 编码方式
- db, err := sql.Open("mysql", "root:12345678@/mydatabase?charset=utf8")
- if err != nil {
- fmt.Println(err)
- }
- defer db.Close()
- /*
- // 查询
- rows, er := db.Query("select id,name,price from myTable")
- // 定义变量
- id := 0
- name := ""
- price := 0
- if er != nil {
- fmt.Println(er)
- }
- // 遍历赋值打印
- for rows.Next() {
- rows.Scan(&id, &name, &price)
- fmt.Println(id, name, price)
- }
- */
- // 查询一行
- /*
- id := 0
- name := ""row := db.QueryRow("select id,name from myTable where id = ? and price = ?", 1, 12212) // 这里的 and 不能替换为,
- row.Scan(&id, &name)
- fmt.Println(id, name)
- */
- // 插入一行
- /*
- ret, _ := db.Exec("insert into myTable(id,name,price) values(3,'aq',90909)")
- ins_id, _ := ret.LastInsertId()
- af, _ := ret.RowsAffected()
- fmt.Println(ins_id, af)
- */
- // 更新数据
- /*
- ret, _ := db.Exec("update myTable set name='qqq'where id = ?", 3)
- ins_id, _ := ret.LastInsertId()
- af, _ := ret.RowsAffected()
- fmt.Println(ins_id, af)
- */
- // 删除数据
- /*
- ret, _ := db.Exec("delete from myTable where id = ?", 3)
- ins_id, _ := ret.LastInsertId()
- af, _ := ret.RowsAffected()
- fmt.Println(ins_id, af)
- */
- /*
- // 预处理 推荐使用预处理
- id := 0
- name := ""stmt, _ := db.Prepare("select id,name from myTable where id = ?")
- row, _ := stmt.Query(2)
- // 注意这里需要 Next(), 不然下面取不到值
- for row.Next() {
- row.Scan(&id, &name)
- fmt.Println(id, name)
- }
- stmt2, _ := db.Prepare("insert into myTable(id,name,price) values(3,?,?)")
- row2, _ := stmt2.Exec("lol", 20002)
- fmt.Println(row2.RowsAffected())
- */
- // 事务
- tx, _ := db.Begin()
- ret, _ := tx.Exec("update myTable set price = price + 1 where id = ?", 1)
- ret1, _ := tx.Exec("update myTable set price = price + 1 where id = ?", 2)
- upd_num1, _ := ret.RowsAffected()
- upd_num2, _ := ret1.RowsAffected()
- if upd_num1> 0 && upd_num2> 0 {
- // 只有两条更新同时成功, Begin 与 Commit 配对, 才会提交
- tx.Commit()
- fmt.Println("Success")
- } else {
- // 否则回滚到 Begin, 提高了安全性
- tx.Rollback()
- fmt.Println("Fail")
- }
- }```
来源: http://www.jianshu.com/p/0acea608b11c