在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ GO/ 5.1 database/sql接口
7 文本處理
3 Web基礎(chǔ)
14 擴(kuò)展Web框架
10.4 小結(jié)
2.2 Go基礎(chǔ)
2.8 總結(jié)
6.1 session和cookie
5.5 使用beedb庫(kù)進(jìn)行ORM開(kāi)發(fā)
8.3 REST
13.6 小結(jié)
5.4 使用PostgreSQL數(shù)據(jù)庫(kù)
14.6 pprof支持
14.1 靜態(tài)文件支持
11.2 使用GDB調(diào)試
7.7 小結(jié)
1 GO環(huán)境配置
14.5 多語(yǔ)言支持
7.1 XML處理
1.5 總結(jié)
13 如何設(shè)計(jì)一個(gè)Web框架
14.3 表單及驗(yàn)證支持
12 部署與維護(hù)
10 國(guó)際化和本地化
1.1 Go 安裝
6.2 Go如何使用session
5.6 NOSQL數(shù)據(jù)庫(kù)操作
6.5 小結(jié)
9.4 避免SQL注入
12.1 應(yīng)用日志
4.2 驗(yàn)證表單的輸入
10.1 設(shè)置默認(rèn)地區(qū)
1.3 Go 命令
9.6 加密和解密數(shù)據(jù)
4.1 處理表單的輸入
4.4 防止多次遞交表單
11.3 Go怎么寫測(cè)試用例
8 Web服務(wù)
12.3 應(yīng)用部署
5.7 小結(jié)
12.5 小結(jié)
11 錯(cuò)誤處理,調(diào)試和測(cè)試
9.2 確保輸入過(guò)濾
14.2 Session支持
6.4 預(yù)防session劫持
12.4 備份和恢復(fù)
8.1 Socket編程
13.1 項(xiàng)目規(guī)劃
13.4 日志和配置設(shè)計(jì)
7.6 字符串處理
13.2 自定義路由器設(shè)計(jì)
6.3 session存儲(chǔ)
3.4 Go的http包詳解
8.2 WebSocket
10.3 國(guó)際化站點(diǎn)
7.5 文件操作
7.4 模板處理
9.1 預(yù)防CSRF攻擊
13.3 controller設(shè)計(jì)
2.6 interface
14.4 用戶認(rèn)證
2.3 流程和函數(shù)
附錄A 參考資料
11.1 錯(cuò)誤處理
9.5 存儲(chǔ)密碼
9.3 避免XSS攻擊
12.2 網(wǎng)站錯(cuò)誤處理
6 session和數(shù)據(jù)存儲(chǔ)
2.4 struct類型
3.3 Go如何使得Web工作
2.5 面向?qū)ο?/span>
3.1 Web工作方式
1.2 GOPATH與工作空間
2.1 你好,Go
9.7 小結(jié)
13.5 實(shí)現(xiàn)博客的增刪改
7.2 JSON處理
10.2 本地化資源
7.3 正則處理
2 Go語(yǔ)言基礎(chǔ)
5.1 database/sql接口
4.5 處理文件上傳
8.5 小結(jié)
4.3 預(yù)防跨站腳本
5.3 使用SQLite數(shù)據(jù)庫(kù)
14.7 小結(jié)
3.2 Go搭建一個(gè)Web服務(wù)器
2.7 并發(fā)
5 訪問(wèn)數(shù)據(jù)庫(kù)
4 表單
3.5 小結(jié)
1.4 Go開(kāi)發(fā)工具
11.4 小結(jié)
9 安全與加密
5.2 使用MySQL數(shù)據(jù)庫(kù)
4.6 小結(jié)
8.4 RPC

5.1 database/sql接口

Go與PHP不同的地方是Go官方?jīng)]有提供數(shù)據(jù)庫(kù)驅(qū)動(dòng),而是為開(kāi)發(fā)數(shù)據(jù)庫(kù)驅(qū)動(dòng)定義了一些標(biāo)準(zhǔn)接口,開(kāi)發(fā)者可以根據(jù)定義的接口來(lái)開(kāi)發(fā)相應(yīng)的數(shù)據(jù)庫(kù)驅(qū)動(dòng),這樣做有一個(gè)好處,只要是按照標(biāo)準(zhǔn)接口開(kāi)發(fā)的代碼, 以后需要遷移數(shù)據(jù)庫(kù)時(shí),不需要任何修改。那么Go都定義了哪些標(biāo)準(zhǔn)接口呢?讓我們來(lái)詳細(xì)的分析一下

sql.Register

這個(gè)存在于database/sql的函數(shù)是用來(lái)注冊(cè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的,當(dāng)?shù)谌介_(kāi)發(fā)者開(kāi)發(fā)數(shù)據(jù)庫(kù)驅(qū)動(dòng)時(shí),都會(huì)實(shí)現(xiàn)init函數(shù),在init里面會(huì)調(diào)用這個(gè)Register(name string, driver driver.Driver)完成本驅(qū)動(dòng)的注冊(cè)。

我們來(lái)看一下mymysql、sqlite3的驅(qū)動(dòng)里面都是怎么調(diào)用的:

//https://github.com/mattn/go-sqlite3驅(qū)動(dòng)
func init() {
    sql.Register("sqlite3", &SQLiteDriver{})
}

//https://github.com/mikespook/mymysql驅(qū)動(dòng)
// Driver automatically registered in database/sql
var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
func init() {
    Register("SET NAMES utf8")
    sql.Register("mymysql", &d)
}

我們看到第三方數(shù)據(jù)庫(kù)驅(qū)動(dòng)都是通過(guò)調(diào)用這個(gè)函數(shù)來(lái)注冊(cè)自己的數(shù)據(jù)庫(kù)驅(qū)動(dòng)名稱以及相應(yīng)的driver實(shí)現(xiàn)。在database/sql內(nèi)部通過(guò)一個(gè)map來(lái)存儲(chǔ)用戶定義的相應(yīng)驅(qū)動(dòng)。

var drivers = make(map[string]driver.Driver)

drivers[name] = driver

因此通過(guò)database/sql的注冊(cè)函數(shù)可以同時(shí)注冊(cè)多個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng),只要不重復(fù)。

在我們使用database/sql接口和第三方庫(kù)的時(shí)候經(jīng)??吹饺缦?

  import (
      "database/sql"
      _ "github.com/mattn/go-sqlite3"
  )

新手都會(huì)被這個(gè)_所迷惑,其實(shí)這個(gè)就是Go設(shè)計(jì)的巧妙之處,我們?cè)谧兞抠x值的時(shí)候經(jīng)常看到這個(gè)符號(hào),它是用來(lái)忽略變量賦值的占位符,那么包引入用到這個(gè)符號(hào)也是相似的作用,這兒使用_的意思是引入后面的包名而不直接使用這個(gè)包中定義的函數(shù),變量等資源。

我們?cè)?.3節(jié)流程和函數(shù)一節(jié)中介紹過(guò)init函數(shù)的初始化過(guò)程,包在引入的時(shí)候會(huì)自動(dòng)調(diào)用包的init函數(shù)以完成對(duì)包的初始化。因此,我們引入上面的數(shù)據(jù)庫(kù)驅(qū)動(dòng)包之后會(huì)自動(dòng)去調(diào)用init函數(shù),然后在init函數(shù)里面注冊(cè)這個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng),這樣我們就可以在接下來(lái)的代碼中直接使用這個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)了。

driver.Driver

Driver是一個(gè)數(shù)據(jù)庫(kù)驅(qū)動(dòng)的接口,他定義了一個(gè)method: Open(name string),這個(gè)方法返回一個(gè)數(shù)據(jù)庫(kù)的Conn接口。

type Driver interface {
    Open(name string) (Conn, error)
}

返回的Conn只能用來(lái)進(jìn)行一次goroutine的操作,也就是說(shuō)不能把這個(gè)Conn應(yīng)用于Go的多個(gè)goroutine里面。如下代碼會(huì)出現(xiàn)錯(cuò)誤

...
go goroutineA (Conn)  //執(zhí)行查詢操作
go goroutineB (Conn)  //執(zhí)行插入操作
...

上面這樣的代碼可能會(huì)使Go不知道某個(gè)操作究竟是由哪個(gè)goroutine發(fā)起的,從而導(dǎo)致數(shù)據(jù)混亂,比如可能會(huì)把goroutineA里面執(zhí)行的查詢操作的結(jié)果返回給goroutineB從而使B錯(cuò)誤地把此結(jié)果當(dāng)成自己執(zhí)行的插入數(shù)據(jù)。

第三方驅(qū)動(dòng)都會(huì)定義這個(gè)函數(shù),它會(huì)解析name參數(shù)來(lái)獲取相關(guān)數(shù)據(jù)庫(kù)的連接信息,解析完成后,它將使用此信息來(lái)初始化一個(gè)Conn并返回它。

driver.Conn

Conn是一個(gè)數(shù)據(jù)庫(kù)連接的接口定義,他定義了一系列方法,這個(gè)Conn只能應(yīng)用在一個(gè)goroutine里面,不能使用在多個(gè)goroutine里面,詳情請(qǐng)參考上面的說(shuō)明。

type Conn interface {
    Prepare(query string) (Stmt, error)
    Close() error
    Begin() (Tx, error)
}

Prepare函數(shù)返回與當(dāng)前連接相關(guān)的執(zhí)行Sql語(yǔ)句的準(zhǔn)備狀態(tài),可以進(jìn)行查詢、刪除等操作。

Close函數(shù)關(guān)閉當(dāng)前的連接,執(zhí)行釋放連接擁有的資源等清理工作。因?yàn)轵?qū)動(dòng)實(shí)現(xiàn)了database/sql里面建議的conn pool,所以你不用再去實(shí)現(xiàn)緩存conn之類的,這樣會(huì)容易引起問(wèn)題。

Begin函數(shù)返回一個(gè)代表事務(wù)處理的Tx,通過(guò)它你可以進(jìn)行查詢,更新等操作,或者對(duì)事務(wù)進(jìn)行回滾、遞交。

driver.Stmt

Stmt是一種準(zhǔn)備好的狀態(tài),和Conn相關(guān)聯(lián),而且只能應(yīng)用于一個(gè)goroutine中,不能應(yīng)用于多個(gè)goroutine。

type Stmt interface {
    Close() error
    NumInput() int
    Exec(args []Value) (Result, error)
    Query(args []Value) (Rows, error)
}

Close函數(shù)關(guān)閉當(dāng)前的鏈接狀態(tài),但是如果當(dāng)前正在執(zhí)行query,query還是有效返回rows數(shù)據(jù)。

NumInput函數(shù)返回當(dāng)前預(yù)留參數(shù)的個(gè)數(shù),當(dāng)返回>=0時(shí)數(shù)據(jù)庫(kù)驅(qū)動(dòng)就會(huì)智能檢查調(diào)用者的參數(shù)。當(dāng)數(shù)據(jù)庫(kù)驅(qū)動(dòng)包不知道預(yù)留參數(shù)的時(shí)候,返回-1。

Exec函數(shù)執(zhí)行Prepare準(zhǔn)備好的sql,傳入?yún)?shù)執(zhí)行update/insert等操作,返回Result數(shù)據(jù)

Query函數(shù)執(zhí)行Prepare準(zhǔn)備好的sql,傳入需要的參數(shù)執(zhí)行select操作,返回Rows結(jié)果集

driver.Tx

事務(wù)處理一般就兩個(gè)過(guò)程,遞交或者回滾。數(shù)據(jù)庫(kù)驅(qū)動(dòng)里面也只需要實(shí)現(xiàn)這兩個(gè)函數(shù)就可以

type Tx interface {
    Commit() error
    Rollback() error
}

這兩個(gè)函數(shù)一個(gè)用來(lái)遞交一個(gè)事務(wù),一個(gè)用來(lái)回滾事務(wù)。

driver.Execer

這是一個(gè)Conn可選擇實(shí)現(xiàn)的接口

type Execer interface {
    Exec(query string, args []Value) (Result, error)
}

如果這個(gè)接口沒(méi)有定義,那么在調(diào)用DB.Exec,就會(huì)首先調(diào)用Prepare返回Stmt,然后執(zhí)行Stmt的Exec,然后關(guān)閉Stmt。

driver.Result

這個(gè)是執(zhí)行Update/Insert等操作返回的結(jié)果接口定義

type Result interface {
    LastInsertId() (int64, error)
    RowsAffected() (int64, error)
}

LastInsertId函數(shù)返回由數(shù)據(jù)庫(kù)執(zhí)行插入操作得到的自增ID號(hào)。

RowsAffected函數(shù)返回query操作影響的數(shù)據(jù)條目數(shù)。

driver.Rows

Rows是執(zhí)行查詢返回的結(jié)果集接口定義

type Rows interface {
    Columns() []string
    Close() error
    Next(dest []Value) error
}

Columns函數(shù)返回查詢數(shù)據(jù)庫(kù)表的字段信息,這個(gè)返回的slice和sql查詢的字段一一對(duì)應(yīng),而不是返回整個(gè)表的所有字段。

Close函數(shù)用來(lái)關(guān)閉Rows迭代器。

Next函數(shù)用來(lái)返回下一條數(shù)據(jù),把數(shù)據(jù)賦值給dest。dest里面的元素必須是driver.Value的值除了string,返回的數(shù)據(jù)里面所有的string都必須要轉(zhuǎn)換成[]byte。如果最后沒(méi)數(shù)據(jù)了,Next函數(shù)最后返回io.EOF。

driver.RowsAffected

RowsAffected其實(shí)就是一個(gè)int64的別名,但是他實(shí)現(xiàn)了Result接口,用來(lái)底層實(shí)現(xiàn)Result的表示方式

type RowsAffected int64

func (RowsAffected) LastInsertId() (int64, error)

func (v RowsAffected) RowsAffected() (int64, error)

driver.Value

Value其實(shí)就是一個(gè)空接口,他可以容納任何的數(shù)據(jù)

type Value interface{}

drive的Value是驅(qū)動(dòng)必須能夠操作的Value,Value要么是nil,要么是下面的任意一種

int64
float64
bool
[]byte
string   [*]除了Rows.Next返回的不能是string.
time.Time

driver.ValueConverter

ValueConverter接口定義了如何把一個(gè)普通的值轉(zhuǎn)化成driver.Value的接口

type ValueConverter interface {
    ConvertValue(v interface{}) (Value, error)
}

在開(kāi)發(fā)的數(shù)據(jù)庫(kù)驅(qū)動(dòng)包里面實(shí)現(xiàn)這個(gè)接口的函數(shù)在很多地方會(huì)使用到,這個(gè)ValueConverter有很多好處:

  • 轉(zhuǎn)化driver.value到數(shù)據(jù)庫(kù)表相應(yīng)的字段,例如int64的數(shù)據(jù)如何轉(zhuǎn)化成數(shù)據(jù)庫(kù)表uint16字段
  • 把數(shù)據(jù)庫(kù)查詢結(jié)果轉(zhuǎn)化成driver.Value值
  • 在scan函數(shù)里面如何把driver.Value值轉(zhuǎn)化成用戶定義的值

driver.Valuer

Valuer接口定義了返回一個(gè)driver.Value的方式

type Valuer interface {
    Value() (Value, error)
}

很多類型都實(shí)現(xiàn)了這個(gè)Value方法,用來(lái)自身與driver.Value的轉(zhuǎn)化。

通過(guò)上面的講解,你應(yīng)該對(duì)于驅(qū)動(dòng)的開(kāi)發(fā)有了一個(gè)基本的了解,一個(gè)驅(qū)動(dòng)只要實(shí)現(xiàn)了這些接口就能完成增刪查改等基本操作了,剩下的就是與相應(yīng)的數(shù)據(jù)庫(kù)進(jìn)行數(shù)據(jù)交互等細(xì)節(jié)問(wèn)題了,在此不再贅述。

database/sql

database/sql在database/sql/driver提供的接口基礎(chǔ)上定義了一些更高階的方法,用以簡(jiǎn)化數(shù)據(jù)庫(kù)操作,同時(shí)內(nèi)部還建議性地實(shí)現(xiàn)一個(gè)conn pool。

type DB struct {
    driver   driver.Driver
    dsn      string
    mu       sync.Mutex // protects freeConn and closed
    freeConn []driver.Conn
    closed   bool
}

我們可以看到Open函數(shù)返回的是DB對(duì)象,里面有一個(gè)freeConn,它就是那個(gè)簡(jiǎn)易的連接池。它的實(shí)現(xiàn)相當(dāng)簡(jiǎn)單或者說(shuō)簡(jiǎn)陋,就是當(dāng)執(zhí)行Db.prepare的時(shí)候會(huì)defer db.putConn(ci, err),也就是把這個(gè)連接放入連接池,每次調(diào)用conn的時(shí)候會(huì)先判斷freeConn的長(zhǎng)度是否大于0,大于0說(shuō)明有可以復(fù)用的conn,直接拿出來(lái)用就是了,如果不大于0,則創(chuàng)建一個(gè)conn,然后再返回之。

上一篇:3.1 Web工作方式下一篇:7.3 正則處理