基本概念
- Open() – creates a DB
- Close() - closes the DB
- Query() - 查詢
- QueryRow() -查詢行
- Exec() -執(zhí)行操作,update,insert,delete
- Row - A row is not a hash map, but an abstraction of a cursor
- Next()
- Scan()
注意:DB并不是指的一個connection
連接到數(shù)據(jù)庫
我們以mysql為例,使用github.com/go-sql-driver/mysql,首先我們需要導(dǎo)入我們需要的包
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
注意我們導(dǎo)入github.com/go-sql-driver/mysql 前面用了一個"_",_操作其實是引入該包,而不直接使用包里面的函數(shù),而是調(diào)用了該包里面的init函數(shù),import的時候其實是執(zhí)行了該包里面的init函數(shù),初始化了里面的變量,_操作只是說該包引入了,我只初始化里面的 init函數(shù)和一些變量,但是往往這些init函數(shù)里面是注冊自己包里面的引擎,讓外部可以方便的使用,就很多實現(xiàn)database/sql的包,在 init函數(shù)里面都是調(diào)用了sql.Register(name string, driver driver.Driver)注冊自己,然后外部就可以使用了。
我們用Open()函數(shù)來打開一個database handle
db, err := sql.Open("mysql", "user:password@tcp(ip:port)/database")
寫一個完整的:
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"log"
)
func main() {
db, err := sql.Open("mysql", "user:password@tcp(ip:port)/database")
if err != nil {
log.Println(err)
}
//在這里進行一些數(shù)據(jù)庫操作
defer db.Close()
}
我們在執(zhí)行Open函數(shù)的時候,并不會去獲得數(shù)據(jù)庫連接有效性,當(dāng)執(zhí)行數(shù)據(jù)庫操作的時候才會去連接,當(dāng)我們需要在Open之后就知道連接的有效性的時候,可以通過Ping()來進行
err = db.Ping()
if err != nil {
log.Println(err)
}
我們通常習(xí)慣使用Close來關(guān)閉數(shù)據(jù)庫連接,但是sql.DB是被設(shè)計成長期有效的類型,我們不應(yīng)該頻繁的Open和Close,相反,我們應(yīng)該建立一個sql.DB,在程序需要進行數(shù)據(jù)庫操作的時候一直使用它,不要在一個方法里面進行Open和Close,應(yīng)該把sql.DB作為參數(shù)傳遞給方法
進行數(shù)據(jù)庫操作
增刪改操作
Exec()方法一般用于增刪改操作,這里以增加為例:
stmt, err := db.Prepare("insert into user(name,age)values(?,?)")
if err != nil {
log.Println(err)
}
rs, err := stmt.Exec("go-test", 12)
if err != nil {
log.Println(err)
}
//我們可以獲得插入的id
id, err := rs.LastInsertId()
//可以獲得影響行數(shù)
affect, err := rs.RowsAffected()
查詢操作
一般的查詢
var name string
var age int
rows, err := db.Query("select name,age from user where id = ? ", 1)
if err != nil {
fmt.Println(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&name, &age)
if err != nil {
fmt.Println(err)
}
}
err = rows.Err()
if err != nil {
fmt.Println(err)
}
fmt.Println("name:", url, "age:", description)
我們應(yīng)該養(yǎng)成關(guān)閉rows的習(xí)慣,在任何時候,都不要忘記rows.Close().哪怕這個rows在確實循環(huán)完之后,已經(jīng)自動關(guān)閉掉了,我們定義rows.Close()也是對我們沒有壞處的,因為我們無法保證,rows是否會正常的循環(huán)完。查詢單條記錄,
我們使用db.QueryRow()
var name string
err = db.QueryRow("select name from user where id = ?", 222).Scan(&name)
沒有結(jié)果的時候會返回err
處理空值
我們用一個name字段為空的記錄來舉例
var name NullString
err := db.QueryRow(
"SELECT name FROM names WHERE id = ?", id).Scan(&name)
if name.Valid {
// use name.String
}
else {
// value
is NULL
}
在這種情況下我們通常使用NullString,但是有時候我們并不關(guān)心值是不是Null,我們只需要吧他當(dāng)一個空字符串來對待就行。這時候我們可以使用[]byte(null byte[]可以轉(zhuǎn)化為空string) 或者 sql.RawBytes,
var col1, col2 []byte
for rows.Next() {
// Scan the value to []byte
err = rows.Scan(&col1, &col2)
if err != nil {
panic(err.Error()) // Just for example purpose. You should use proper error handling instead of panic
}
// Use the string value
fmt.Println(string(col1), string(col2))
}
使用*sql.RawBytes
package main
import (
"database/sql" "fmt" _
"github.com/go-sql-driver/mysql")
func main() {
// Open database connection
db, err := sql.Open(
"mysql",
"user:password@/dbname")
if err != nil {
panic(err.Error()) // Just
for example purpose. You should use proper error handling instead of panic
}
defer db.Close()
// Execute the query
rows, err := db.Query(
"SELECT * FROM table")
if err != nil {
panic(err.Error()) // proper error handling instead of panic
in your app
}
// Get column names
columns, err := rows.Columns()
if err != nil {
panic(err.Error()) // proper error handling instead of panic
in your app
}
// Make a slice
for the values
values := make([]sql.RawBytes, len(columns))
// rows.Scan wants
'[]interface{}' as an argument, so we must copy the
// references into such a slice
// See http://code.google.com/p/go-wiki/wiki/InterfaceSlice
for details
scanArgs := make([]interface{}, len(values))
for i := range values {
scanArgs[i] = &values[i]
}
// Fetch rows
for rows.Next() {
// get RawBytes
from data
err = rows.Scan(scanArgs

)
if err != nil {
panic(err.Error()) // proper error handling instead of panic
in your app
}
// Now do something with the data.
// Here we just
print each column as a string.
var value string
for i, col := range values {
// Here we can check
if the value
is nil (NULL value)
if col == nil {
value =
"NULL" }
else {
value = string(col)
}
fmt.Println(columns[i],
": ", value)
}
fmt.Println(
"-----------------------------------")
}
if err = rows.Err(); err != nil {
panic(err.Error()) // proper error handling instead of panic
in your app
}
}
事務(wù)
使用db.Begin()來開啟一個事務(wù), 通過Commit()和Rollback()方法來關(guān)閉。
tx := db.Begin()
tx.Rollback()
tx.Commit()
Exec, Query, QueryRow and Prepare 方法已經(jīng)全部可以在tx上面使用。使用方法和在*sql.DB是一樣的,事務(wù)必須以Commit()或者Rollback()結(jié)束
The Connection Pool
在database/sql中有一個很基本的連接池,你并沒有多大的控制權(quán),僅僅可以設(shè)置SetMaxIdleConns和SetMaxOpenConns,也就是最大空閑連接和最大連接數(shù)。
db.SetMaxIdleConns(n)
db.SetMaxOpenConns(n)