基本概念
- Open() – creates a DB
- Close() - closes the DB
- Query() - 查詢
- QueryRow() -查詢行
- Exec() -執行操作,update,insert,delete
- Row - A row is not a hash map, but an abstraction of a cursor
- Next()
- Scan()
注意:DB并不是指的一個connection
連接到數據庫
我們以mysql為例,使用github.com/go-sql-driver/mysql,首先我們需要導入我們需要的包
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
注意我們導入github.com/go-sql-driver/mysql 前面用了一個"_",_操作其實是引入該包,而不直接使用包里面的函數,而是調用了該包里面的init函數,import的時候其實是執行了該包里面的init函數,初始化了里面的變量,_操作只是說該包引入了,我只初始化里面的 init函數和一些變量,但是往往這些init函數里面是注冊自己包里面的引擎,讓外部可以方便的使用,就很多實現database/sql的包,在 init函數里面都是調用了sql.Register(name string, driver driver.Driver)注冊自己,然后外部就可以使用了。
我們用Open()函數來打開一個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)
}
//在這里進行一些數據庫操作
defer db.Close()
}
我們在執行Open函數的時候,并不會去獲得數據庫連接有效性,當執行數據庫操作的時候才會去連接,當我們需要在Open之后就知道連接的有效性的時候,可以通過Ping()來進行
err = db.Ping()
if err != nil {
log.Println(err)
}
我們通常習慣使用Close來關閉數據庫連接,但是sql.DB是被設計成長期有效的類型,我們不應該頻繁的Open和Close,相反,我們應該建立一個sql.DB,在程序需要進行數據庫操作的時候一直使用它,不要在一個方法里面進行Open和Close,應該把sql.DB作為參數傳遞給方法
進行數據庫操作
增刪改操作
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()
//可以獲得影響行數
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)
我們應該養成關閉rows的習慣,在任何時候,都不要忘記rows.Close().哪怕這個rows在確實循環完之后,已經自動關閉掉了,我們定義rows.Close()也是對我們沒有壞處的,因為我們無法保證,rows是否會正常的循環完。查詢單條記錄,
我們使用db.QueryRow()
var name string
err = db.QueryRow("select name from user where id = ?", 222).Scan(&name)
沒有結果的時候會返回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,但是有時候我們并不關心值是不是Null,我們只需要吧他當一個空字符串來對待就行。這時候我們可以使用[]byte(null byte[]可以轉化為空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
}
}
事務
使用db.Begin()來開啟一個事務, 通過Commit()和Rollback()方法來關閉。
tx := db.Begin()
tx.Rollback()
tx.Commit()
Exec, Query, QueryRow and Prepare 方法已經全部可以在tx上面使用。使用方法和在*sql.DB是一樣的,事務必須以Commit()或者Rollback()結束
The Connection Pool
在database/sql中有一個很基本的連接池,你并沒有多大的控制權,僅僅可以設置SetMaxIdleConns和SetMaxOpenConns,也就是最大空閑連接和最大連接數。
db.SetMaxIdleConns(n)
db.SetMaxOpenConns(n)