GO語言學習(九)
上一期我們了解了實現web的工作中極為重要的net/http抱的細節講解,大家學會了實現web開發的一些底層基礎知識,在這一期我來為大家講解一下web工作的一個重要方法,:使用數據庫,現在就讓我來為大家講解這一篇章,歡迎大家交流學習
sql接口及database
首先和大家解釋一下在golang中沒有提供現成的數據庫驅動方式,英雌在我們實際開發中一般是實現接口,利用這些接口來實現相應的數據庫驅動操作,這樣使用可以在遷移數據庫的時候,只用使用開發好的標準數據庫接口。
sql.register說明
這個存在于database/sql的函數是用來注冊數據庫驅動的,當第三方開發者開發數據庫驅動時,都會實現init函數,在init里面會調用這個
Register(name string, driver driver.Driver)
完成本驅動的注冊。
我們來看一下mysql、sqlite3的驅動里面都是怎么調用的,下面直接上代碼:
// 實現示例:
//https://github.com/mattn/go-sqlite3驅動
func init() {sql.Register("sqlite3", &SQLiteDriver{})
}//https://github.com/mikespook/mymysql驅動
// 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)
}
**我們看到第三方數據庫驅動都是通過調用這個函數來注冊自己的數據庫驅動名稱以及相應的driver實現。在database/sql內部通過一個map來存儲用戶定義的相應驅動。**示例說明:
var drivers = make(map[string]driver.Driver)drivers[name] = driver
因此通過database/sql的注冊函數可以同時注冊多個數據庫驅動,只要不重復。
我們一般通過使用以下代碼來使用相應的接口和第三方庫:
import ("database/sql"_ "github.com/mattn/go-sqlite3")
一般的新手都會被這個_所迷惑,其實這個就是Go設計的巧妙之處,我們在變量賦值的時候經常看到這個符號,它是用來忽略變量賦值的占位符,那么包引入用到這個符號也是相似的作用,這兒使用_的意思是引入后面的包名而不直接使用這個包中定義的函數,變量等資源。同時我們引入上面的數據庫驅動包之后會自動去調用init函數,然后在init函數里面注冊這個數據庫驅動,這樣我們就可以在接下來的代碼中直接使用這個數據庫驅動了。
driver.Driver講解
Driver是一個數據庫驅動的接口,他定義了一個method: Open(name string),這個方法返回一個數據庫的Conn接口。
type Driver interface {Open(name string) (Conn, error)
}
返回的Conn只能用來進行一次goroutine的操作,也就是說不能把這個Conn應用于Go的多個goroutine里面。如下代碼會出現錯誤,如下所示:
...
go goroutineA (Conn) //執行查詢操作
go goroutineB (Conn) //執行插入操作
...
上面這樣的代碼可能會使Go不知道某個操作究竟是由哪個goroutine發起的,從而導致數據混亂,比如可能會把goroutineA里面執行的查詢操作的結果返回給goroutineB從而使B錯誤地把此結果當成自己執行的插入數據。
第三方驅動都會定義這個函數,它會解析name參數來獲取相關數據庫的連接信息,解析完成后,它將使用此信息來初始化一個Conn并返回它。
driver.Conn說明
Conn是一個數據庫連接的接口定義,他定義了一系列方法,這個Conn只能應用在一個goroutine里面,不能使用在多個goroutine里面.
type Conn interface {Prepare(query string) (Stmt, error)Close() errorBegin() (Tx, error)
}
**1.**Prepare函數返回與當前連接相關的執行Sql語句的準備狀態,可以進行查詢、刪除等操作。
**2.**Close函數關閉當前的連接,執行釋放連接擁有的資源等清理工作。因為驅動實現了database/sql里面建議的conn pool,所以你不用再去實現緩存conn之類的,這樣會容易引起問題。
**3.**Begin函數返回一個代表事務處理的Tx,通過它你可以進行查詢,更新等操作,或者對事務進行回滾、遞交。
driver.Stimt詳解
**Stmt是一種準備好的狀態,和Conn相關聯,而且只能應用于一個goroutine中,不能應用于多個goroutine。**實現代碼如下:
type Stmt interface {Close() errorNumInput() intExec(args []Value) (Result, error)Query(args []Value) (Rows, error)
}
Close函數關閉當前的鏈接狀態,但是如果當前正在執行query,query還是有效返回rows數據。
NumInput函數返回當前預留參數的個數,當返回>=0時數據庫驅動就會智能檢查調用者的參數。當數據庫驅動包不知道預留參數的時候,返回-1。
Exec函數執行Prepare準備好的sql,傳入參數執行update/insert等操作,返回Result數據
Query函數執行Prepare準備好的sql,傳入需要的參數執行select操作,返回Rows結果集
driver.Tx解釋
事務處理一般就兩個過程,遞交或者回滾。數據庫驅動里面也只需要實現這兩個函數就可以,代碼如下:
type Tx interface {Commit() errorRollback() error
}
**這兩個函數一個用來遞交一個事務,一個用來回滾事務。**大家可以在實際開發中感受一下這個的獨特特性,不懂的歡迎大家在評論區中分享,大家一起討論。
driver.Execer講解
driver.Execer是一個Conn可選擇實現的接口,功能有許多好的妙用,如果未使用這個接口,那么在調用DB.Exec,就會首先調用Prepare返回Stmt,然后執行Stmt的Exec,然后關閉Stmt。下面提供實現這個接口的示例代碼:
type Execer interface {Exec(query string, args []Value) (Result, error)
}
driver.Result講解
driver.Result是執行Update/Insert等操作返回的結果接口定義,LastInsertId函數返回由數據庫執行插入操作得到的自增ID號。RowsAffected函數返回執行Update/Insert等操作影響的數據條目數。下面我們來為大家提供一下示例代碼:
type Result interface {LastInsertId() (int64, error)RowsAffected() (int64, error)
}
driver.Rows講解
其實Rows是執行查詢返回的結果集接口定義,Columns函數返回查詢數據庫表的字段信息,這個返回的slice和sql查詢的字段一一對應,而不是返回整個表的所有字段。Close函數用來關閉Rows迭代器。Next函數用來返回下一條數據,把數據賦值給dest。dest里面的元素必須是driver.Value的值除了string,返回的數據里面所有的string都必須要轉換成[]byte。如果最后沒數據了,Next函數最后返回io.EOF。示例代碼如下:
type Rows interface {Columns() []stringClose() errorNext(dest []Value) error
}
driver.RowsAffected講解
RowsAffected其實就是一個int64的別名,但是他實現了Result接口,用來底層實現Result的表示方式,代碼如下:
type RowsAffected int64func (RowsAffected) LastInsertId() (int64, error)func (v RowsAffected) RowsAffected() (int64, error)
driver.Value講解
Value其實就是一個空接口,他可以容納任何的數據,然后其實就是drive的Value是驅動必須能夠操作的Value,Value要么是nil,要么是下面的給出的數據類型。
type Value interface{} // 構建value接口// 數據類型
int64
float64
bool
[]byte
string [*]除了Rows.Next返回的不能是string.
time.Time
database/sql說明
database/sql在database/sql/driver提供的接口基礎上定義了一些更高階的方法,用以簡化數據庫操作,同時內部還建議性地實現一個conn. pool,然后我們就可以得出一下結論,大家可以自己先思考一下,在結合我給的講解:
我們可以看到Open函數返回的是DB對象,里面有一個freeConn,它就是那個簡易的連接池。它的實現相當簡單或者說簡陋,就是當執行
db.prepare
->db.prepareDC
的時候會defer dc.releaseConn
,然后調用db.putConn
,也就是把這個連接放入連接池,每次調用db.conn
的時候會先判斷freeConn的長度是否大于0,大于0說明有可以復用的conn,直接拿出來用就是了,如果不大于0,則創建一個conn,然后再返回之。
看完這些我在給大家提供代碼,大家可以參考,然后自己在試著敲敲下面的示例代碼:
type DB struct {driver driver.Driverdsn stringmu sync.Mutex // protects freeConn and closedfreeConn []driver.Connclosed bool
}
結語
我們這一期簡單的學習了一下數據庫連接的接口實現,并且為大家講解了些許的相關接口的實現,還有更多的知識大家需要自探索,后面我也會出一個專題來為大家詳細解釋的。
下一期我會為大家講解實現MYSQL數據庫,同時作為目前Internet上流行的網站構架方式是LAMP,其中的M即MySQL, 作為數據庫,MySQL以免費、開源、使用方便為優勢成為了很多Web開發的后端數據庫存儲引擎。歡迎大家期待與認可
在此謝謝大家的支持,你的關注和點贊會是我繼續努力寫文章的動力,也感謝大家能夠持續關注博主,謝謝大家的支持,周末會給大家寫一篇福利文章,大家敬請期待~~~