前言:Go標準庫下的database/sql包爲我們操作各類數據庫提供了一個統一操作的接口,各大數據庫開發商據此包提供相關的數據庫驅動實現。我們開發者下載安裝所需驅動,調用sql包下相關api即可對數據庫操作。其優點爲不同數據庫的操作可公用一套代碼,切換驅動包即可。
一,加載數據庫驅動,測試鏈接是否正常
- 到官網下載mysql驅動到本地 (以mysql數據庫爲列)https://github.com/go-sql-driver/mysql
go get -u github.com/go-sql-driver/mysql
- 匿名導入數據庫驅動包,註冊驅動
import _ "github.com/go-sql-driver/mysql" //執行init(),註冊mysql驅動
- Open(driverName,dsn)方法校驗倆參數是否正確,返回數據庫操作句柄*DB
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" //執行init(),註冊mysql驅動 ) func main(){ dsn := "root:root@tcp(127.0.0.1:3306)/dbname"; Db,err = sql.Open("mysql",dsn) if err!=nil{ panic(error.Error(err)) } fmt.Println("parameter verification succeeded!") }
注:
1.dsn爲操作的數據庫資源名稱,格式爲 [username[:password]@][protocol[(address)]]/dbname[?param1=value1&…¶mN=valueN]
詳情可到https://github.com/go-sql-driver/mysql 查看.
2.sql.Open()方法只是做參數的校驗(即驅動名是否與註冊的驅動相同),並沒有打開一個數據庫鏈接。我們可到源碼的註釋裏可以看到:
// Open may just validate its arguments without creating a connection
// to the database. To verify that the data source name is valid, call
// Ping. - Ping()方法測試數據庫是否鏈接成功(校驗dsn參數是否正確有效)
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" //執行init(),註冊mysql驅動 ) func main(){ //獲取數據庫操作句柄*Db dsn := "root:root@tcp(127.0.0.1:3306)/dbname"; Db,err = sql.Open("mysql",dsn) if err!=nil{ panic(error.Error(err)) } fmt.Println("driverName verification succeeded!") //測試數據庫鏈接是否成功,ping方法會去鏈一次數據庫以校驗dsn是否正確 if pinErr := Db.Ping(); pinErr != nil { fmt.Println("數據庫鏈接失敗,請檢查dsn",pinErr) }else { fmt.Println("數據庫鏈接測試通過,可用!") } }
Open,Ping方法調用過程可簡記爲【兩參校驗法】,即driverName,dsn參數校驗。只有在這兩步都成功後後續數據庫的CURD才能相繼展開。
二,數據庫的CURD
1. 非查詢類的api操作
非查詢類顧名思義即爲增刪改相關的數據庫操作,使用Db.Exec(sql,…interface{})方法來實現,sql語句即爲增刪改的語句,參數二爲需要傳入sql語句裏的值。方法返回Result一個結構體對象其包含了一個獲取受影響行數和獲取該條記錄id的方法。
- Db.Exec(sql,…interface{}) 增刪改業務執行的方法,傳入相關sql語句執行即可
- Db.Prepare(sql),Stmt.Exec() ,Stmt.Close() 預編譯處理,多次執行
//普通版本
func (user *User) AddUser1()(result sql.Result,err error){
sql := `insert into user(username,password,email) values(?,?,?)`
result,err = Db.Exec(sql,user.Username,user.Password,user.Email)
return
}
//預編譯sql版:一次編譯,多次執行。eg:循環執行插入
func (user *User) AddUser2()(result sql.Result,err error){
//1.編寫sql語句
sql := `insert into user(username,password,email) values(?,?,?)`
//2.預編譯,準備
stmt,err := Db.Prepare(sql)
if err!=nil{
fmt.Println("預編譯出現異常:",err)
}
//執行,傳參數填充佔位符?
result,err = stmt.Exec(user.Username,user.Password,user.Email)
//關閉資源
defer stmt.Close()
return
}
2. 查詢類的api操作
- 單條查詢
- Db.QueryRow() 查詢一條記錄,返回Row結果
- row.Scan() 將查詢到的記錄值掃描到傳入參數中
//查詢一條用戶 func (user *User) FindUserById()User{ sql := `select id,username,password,email from user where id = ?` row := Db.QueryRow(sql,user.Id) var id int var username,password,email string err := row.Scan(&id,&username,&password,&email) if err != nil { fmt.Println("查詢單條記錄失敗",err) } return User{Id:id,Username:username,Password:password,Email:email} }
- 多條查詢
- Db.Query() 查詢多條記錄,返回Rows結果集
- rows.Next() 移動到下一條記錄,以便Scan()讀取
- row.Scan() 讀取記錄的字段值到參數
- rows.Close() 關閉資源
//查詢所有用戶 func (user *User) FindUsers()[]*User{ sql := `select *from user` rows,err := Db.Query(sql) if err!=nil { fmt.Println("查詢所有記錄失敗",err) return nil } //聲明切片 var users []*User for rows.Next(){ var id int var username,password,email string err = rows.Scan(&id,&username,&password,&email) if err != nil { fmt.Println("掃描失敗") return nil } //添加到切片 users = append(users,&User{Id:id,Username:username,Password:password,Email:email}) } defer rows.Close() return users }
3. 數據庫null值的處理
1.因爲數據庫字段的值可能存在設置爲可null的情況,而Go語言是強類型的語言在其基礎數據類型中沒有匹配null的數據類型。此時我們可以用database/sql包下提供的NullString,NullFloat64,NullInt32,NullBool,NullTime結構體類型來代替相關變量的數據類型,來解決此問題。
2.其中各結構體裏有兩個成員變量,一個是各自類型的變量值,一個bool類型的Valid(當其值不爲null時爲true)。
4. 預編譯,預處理sql
一次編譯,多次執行。eg:循環執行插入
- func (db *DB) Prepare(query string) (*Stmt, error) //預編譯sql
- func (s *Stmt) Exec(args …interface{}) (Result, error) //stmt對象來傳入參數執行
- func (s *Stmt) Close() error //關閉資源
- 當然還有單查詢和多條查詢也可以先預編sql。對於多次的執行來提高執行效率
三,事務
- func (db *DB) Begin() (*Tx, error) 開始事務
- func (tx *Tx) Commit() error 提交事務
- func (tx *Tx) Rollback() error 回滾中止事務
注:CURD方法也由*DB調用變成爲 *Tx對象調用