# 前言
为了匹配爬虫和网络编程学习,开始看一些库,比如数据库操作什么的。同时网上找到的资料感觉似乎有些问题,不和我胃口,或者说有些小细节需要多找一些资料以及结合官方档案才能找出答案,所以就写下这个东西
其实github上的大部分第三方库,都可以在[官方文档](https://godoc.org/github.com)里找到他的文档。。。
# 内容
## 开始mysql
### 引用驱动与打开数据库
标准库有一个database/sql提供了接口,但是它没有具体的数据库驱动。想要操作具体数据库还需要自己再去搞第三方。比如mysql就需要`go-sql-driver /mysql `
下载地址[mysql](https://github.com/go-sql-driver/mysql)
当然,这里的mysql驱动只是一个驱动,函数还是使用标准库内的函数。所以只是匿名引用即
`_"github.com/go-sql-driver/mysql"`
在引用了驱动后,想要使用数据库还需要一个连接池。
`db.err := sql.Open("mysql",user:password@tcp(host:port)/database?charset=utf8)`
这里前面的参数是指使用何种数据库驱动,后面的参数叫DSN这是第三方的mysql驱动提供的具体内容,用于打开数据库。open返回一个连接池对象,不是单个连接。在open的时候并没有去连接数据库,只有在执行query、exce方法的时候才会去实际连接数据库
DSN参数内容:`USER、PASSWORD、HOST、PORT、DATABASE、CHARSET`没错,那个tcp是必须的,或许可以变换但是我没查到资料_(:з」∠)_
Open的返回值为*DB与error。如果是通过函数打开的数据库想要返回一个db的话
`func opensql() *sql.DB`
记得return db就行了
<hr/>
#### 一个小点
golang里面fmt.sprintf输出的字符串,可以直接做一个字符串来用。
比如:
`dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s", USER_NAME, PASS_WORD, HOST, PORT, DATABASE, CHARSET)`
dbDSN就可以拿出去做一个字符串变量用了。
<hr/>
另外,`Open`操作是可以关闭的,即`db.Close()`。不过因为DB句柄通常被多个go程共享,并长期活跃,所以一般不关db。
Open操作只是一次打开,它不会对参数内容的正确性做出检查,所以只用open是无法知道我们是否成功使用数据库,还需要使用ping来检验。
`db.Ping()`它会检测连通性和账户密码正确与否,对应数据库是否存在之类的,返回err,直接用`if err != nil`来判断就行
## 操作
### 多行操作
`rows,err := db.Query("select xx from xx where xx = ?",xx)`
Query执行一次查询,会返回多行结果,一般用于使用select语句。里面的参数都可以用?代替,然后在后面补充?对应的量,如上。
在执行查询后,一般都会在底下放上一个`defer rows.Close()`来关闭查询。
Query的返回rows,是无法直接输出的(至少我的测试里是输出不了正确值)需要通过`rows.Next()`来解决
for rows.Next() {
var xx string
if err := rows.Scan(&xx);err != nil {
log.Fatal(err)
}
fmt.Printf(xx)
}
这里需要注意scan返回的是err值,而不是rows查询到的值。但是我们需要Scan(&xx)这个行为来将查询到的值绑定到xx这个新创建的变量上。
可以在补充一个`err := rows.Err()`来检测查询时有无错误。Err返回可能的、在迭代时出现的错误。Err需在显式或隐式调用Close方法后调用。
<hr>
#### 一个小点
log.Fatal与panic 都是用于出现错误时终止程序的,但是两者有些许不同。
log.Fatal的过程是:打印输出,退出程序。 这里defer函数不会执行
panic是:停止函数执行(不是程序),defer执行,返回给调用者,调用者也执行panic,开始递归到程序终止
<hr>
### 单行查询
多行查询虽然很好用,但是如果我们只需要一个结果的话,未免有些麻烦。所以就使用单行查询
`err = db.QueryRow("select SCHEMA_NAME from information_schema.SCHEMATA where SCHEMA_NAME = ?",DATABASE).Scan(&name)`
这里使用的例子是查schema确定目标数据库是否存在。
我们可以看到,单行查询是直接在后面接Scan的,而不是
row := db.QueryRow(...)
err := row.Scan(xx)
我也尝试过这种做法,但是QueryROw的返回没有Scan可选。。。
查到的东西就可以直接输出了,不过为了进行一些判定,可以写个switch。
同时如果目标不存在的话,可以通过`err == sql.ErrNoRows`来整活。
### 命令执行
执行用Exec函数,该函数会执行一次命令,但是不会返回任何执行结果。
举例:插入数据
r,err := db.Exec("insert into news values(?,?,?)","blog","http://www.y1nhui.com","2020-3-17")
if err != nil {
panic(err)
}
fmt.Println(r.RowsAffected())
事实上,第一句就应该执行数据的,但是我们的r不使用就会报错,所以写一个println来调用一下r。
同时这里我们发现,r存在rowsaffected()与LastInsertId()两个。他们分别指收影响的行数和插入的id
# 结语
其实常用的应该还有一个Prepare,用于事务,不过目前这个东西对我来说不常用,所以就不写上去了。。。。
如果文章内容有些问题,多谢斧正。
同时如果看完还是有些疑惑也欢迎提出
参考资料:......太多了写不完,我就贴上官方文档汉化吧。
[中文库](https://studygolang.com/pkgdoc)
有疑问加站长微信联系(非本文作者))