Go - 數據庫原生操作包database/sql

前言: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&…&paramN=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對象調用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章