1. 程式人生 > >Go Web程式設計:訪問資料庫

Go Web程式設計:訪問資料庫

第一次接觸資料庫的操作,很多概念都是陌生的,如果你跟我一樣,請先移步:MySql入門(1) ,初步瞭解資料庫的操作。本文後面的介紹將以mysql為主。

Go沒有內建操作資料庫的驅動,僅僅定義了一套database/sql介面,使用者開發時需要基於驅動介面開發相應的資料庫驅動,Go定義的這些驅動介面參考原始碼:driver.go。我沒有去研究這些介面的開發,而是實行拿來主義,因為github上已經有很多開源的驅動:SQLDrivers,比如Go Web程式設計推薦的MySql驅動:https://github.com/go-sql-driver/mysql/,本文的例子也是採用這個驅動。

準備工作

建立一個數據庫表結構:資料庫test,使用者表test_table。

[email protected]:~$ mysql -h localhost -u root -p //root使用者連線mysql,建立test資料庫,設定許可權jindg使用者能訪問和操作資料庫test
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.6.31-0ubuntu0.15.10.1 (Ubuntu)

Copyright (c) 2000, 2016, Oracle and/or its affiliates.
All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>create database test; Query OK, 1 row affected (0.00 sec) mysql>
grant all on test.* to [email protected] identified by '123'; Query OK, 0 rows affected (0.04 sec) mysql> exit Bye [email protected]:~$ mysql -h localhost -u jindg -p //jindg使用者連線mysql,在test下建立一張表 Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 7 Server version: 5.6.31-0ubuntu0.15.10.1 (Ubuntu) Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | test | | testdb | +--------------------+ 3 rows in set (0.00 sec) mysql> use test Database changed mysql> create table test_table(name vchar(40), age int) -> ; ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'vchar(40), age int)' at line 1 mysql> create table test_table(id bigint, name varchar(40), age int) -> ; Query OK, 0 rows affected (0.22 sec) mysql> alter table test_table add primary key (id) -> ; //忘記設主鍵了 Query OK, 0 rows affected (0.45 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> alter table test_table modify id bigint auto_increment; //設自動增加 Query OK, 0 rows affected (0.45 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> describe test_table; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(40) | YES | | NULL | | | age | int(11) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ 3 rows in set (0.00 sec)

連線資料庫

package main

import (
    "database/sql"
    "fmt"
    _ "github.com/Go-SQL-Driver/MySQL"
    //"time"
)

func main() {
    db, err := sql.Open("mysql", "jindg:[email protected](localhost:3306)/test")
    checkErr(err)

    err = db.Ping()
    if err == nil {
        fmt.Println("ping is ok")
    }
    checkErr(err)
    defer db.Close()
}
func checkErr(err error) {
    if err != nil {
        panic(err)
    }
}

輸出:

ping is ok

通過上面的程式碼可以看出,連線資料庫其實很簡單,一個Open函式就可以了。其中幾個關鍵要點【其實是我學習中碰到的各種問題】我解釋下:
1. import _ “github.com/Go-SQL-Driver/MySQL”:
這裡有一個下劃線,它的功能是表示當前程式不引用該包,而是呼叫它的init函式而已。當你執行go build或者go get時,會從github.com/Go-SQL-Driver/MySQL把原始碼下下來編譯成一個庫檔案放在pkg目錄下,比如我的系統在如下目錄:

jindg@nc:~/goWorkProjects/goPath/pkg/linux_amd64/github.com/Go-SQL-Driver$ ls
MySQL.a

更多的參考:go-import下劃線的作用import 下劃線 什麼作用
2. sql.Open
sql.Open的第一個引數是driver的名稱;第二個引數是DSN (Data Source Name),用於連線資料的資訊。這裡重點是DSN的格式。在瞭解DSN格式前,先閱讀DSN (Data Source Name)PEAR DB ,他們會幫助你建立DSN的概念。
DSN的格式:

[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...&paramN=valueN]

如何獲取mysql的埠,有兩種方法:一種是通過netstat -apn命令檢視端口占用情況,如下:

jindg@nc:~/daly_wiki/linux$ sudo netstat -apn|grep mysql
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      29501/mysqld    
unix  2      [ ACC ]     流        LISTENING     1602657  29501/mysqld        /var/run/mysqld/mysqld.sock
unix  3      [ ]         流        已連線     1814150  29501/mysqld        /var/run/mysqld/mysqld.sock
unix  3      [ ]         流        已連線     1814540  8870/mysql

另一種是檢視mysql的配置檔案:

vim /etc/mysql/mysql.conf.d/mysqld.cnf

查詢port,就能找到mysql所用的埠號。

3. db.Ping”:
Ping平時我們是用來檢測網路通不通,這裡的功能也一樣,用來檢測資料庫連線是否正常。那問題就來了,為什麼Open成功了,還需要Ping檢測呢?我們來看看Open的原始碼,如下圖:
這裡寫圖片描述
選中的部分翻譯過來意思是:Open只是驗證了傳入的引數,而不建立一個連線到資料庫。要驗證資料來源是否有效,呼叫Ping介面。
可以嘗試將上面的程式碼中第二個引數的密碼修改成錯誤的密碼,會發現 Open介面不會返回任何錯誤,直到Ping才會報錯。

操作資料庫

1. Query”:
用於查詢,如果要問一個數據庫的問題,並將返回一組行,即使它是空的。可以用Query介面實現,但是如果不返回行不應使用查詢功能;他們應該使用exec()。
先來看看查詢的例子,在上面的main方法中加入如下查詢語句:

    rows, err := db.Query("describe test_table")
    fmt.Println(rows)
    defer rows.Close()

    for rows.Next() {
        var fileId1 string
        var fileId2 string
        var fileId3 string
        var fileId4 string
        var fileId5 interface{}
        var fileId6 string
        err := rows.Scan(&fileId1, &fileId2, &fileId3, &fileId4, &fileId5, &fileId6)
        fmt.Println(fileId1, fileId2, fileId3, fileId4, fileId5, fileId6)
        checkErr(err)
    }

對應的程式碼輸出:

id bigint(20) NO PRI <nil> auto_increment
name varchar(40) YES  <nil> 
age int(11) YES  <nil>

上面的程式碼實現了檢索表結構功能:describe test_table。Query的引數其實就是我們平時操作資料的命令字,並且它還可以使用帶佔位符?的引數,比如:

hql:="select * from tb_user where name = ?"
_,err:=db.Query(hql,"viney")
checkErr(err)

mysql中的佔位符是?,PostgreSQL的佔位符為$N,注意領會它的使用。

為什麼要顯示的呼叫rows.Close()?在結果集(rows)未關閉前,底層的連線處於繁忙狀態。當遍歷讀到最後一條記錄時,會發生一個內部EOF錯誤,自動呼叫rows.Close(),但是如果提前退出迴圈,rows不會關閉,連線不會回到連線池中,連線也不會關閉。所以手動關閉非常重要。rows.Close()可以多次呼叫,是無害操作。

Next()方法和Scan()方法如何關聯?使用Scan()方法遍歷返回行的內容。至於Next()方法和Scan()方法如何關聯的,去看它的原始碼,不難發現,Next()會把當前行的內容放入Row結構體中成員lastcols,然後呼叫Scan()從Row結構體中成員lastcols取出來

2. Exec”:
Exec()方法一幫用於插入、刪除等沒有返回行的操作,比如插入:

 _, err = db.Exec("insert into test_table(name, age) values('zhang shan', '20')")

上面是直接往資料庫插入一條資訊。

3. Prepare”:
我們在插入大量資料時,採用批量插入所花的時間肯定比一條一條的插入要省時間一些,原因是批量,是所有的資料都在記憶體,然後一起提交資料庫寫入硬碟,而一條一條的插入每一次都會與資料庫、硬碟打交道等,每一次都會做一些重複的操作,自然會費時很多。那在Go中如何實現批量操作呢?
Exec、Prepare區別?
在使用Prepare前,先了解直接db.Exec()和先Prepre後Exec的區別,先來看直接呼叫db.Exec()的原始碼,如下:
這裡寫圖片描述
可以看到每次呼叫Exec()方法,都會先呼叫Prepare()方法先建立一個預處理語句,然後呼叫resultFromStatement()方法執行sql操作。
再來看Prepare後的Exec的原始碼,如下:
這裡寫圖片描述
很明顯,這裡是直接呼叫resultFromStatement()方法,並不再需要Prepare,說明簽名的Prepare執行後,連線會一直在,即使連線不在了,也會自動re-prepare。因此在大資料操作的時候,採用此方法會省時不少。
一個簡單的例子實現

    stmt, err := db.Prepare("insert into test_table(name, age) values(?,?)")
    checkErr(err)
    _, err = stmt.Exec("li si", 21)
    checkErr(err)
    _, err = stmt.Exec("wang ma zi", 22)
    checkErr(err)

注意佔位符?的使用。

事務

事務的概念屬於資料庫,而不屬於Go。資料庫中事務的提出是為了解決決併發情況下保持資料一致性的問題,具體概念細節請閱讀資料庫事務
db.Begin()開始事務,Commit() 或 Rollback()關閉事務。Tx從連線池中取出一個連線,在關閉之前都是使用這個連線。Tx不能和DB層的BEGIN, COMMIT混合使用。

如果你需要通過多條語句修改連線狀態,你必須使用Tx,例如:

建立僅對單個連線可見的臨時表
設定變數,例如SET @var := somevalue
改變連線選項,例如字符集,超時

Exec, Query, QueryRow and Prepare 方法已經全部可以在tx上面使用。使用方法和在*sql.DB是一樣的,事務必須以Commit()或者Rollback()結束,使用事務進行大批量資料操作的時候,你會發現它的處理速度最快,效率更高,比前面提到的方法還要更有效率。來看一個例子:

    tx, err := db.Begin()
    checkErr(err)
    stmt, err := db.Prepare("insert into test_table(name, age) values(?,?)")
    checkErr(err)
    _, err = stmt.Exec("li si", 21)
    checkErr(err)
    _, err = stmt.Exec("wang ma zi", 22)
    checkErr(err)
    //err = tx.Commit()
    //checkErr(err)
    err = tx.Rollback()
    checkErr(err)

參考:

相關推薦

Go Web程式設計訪問資料庫

第一次接觸資料庫的操作,很多概念都是陌生的,如果你跟我一樣,請先移步:MySql入門(1) ,初步瞭解資料庫的操作。本文後面的介紹將以mysql為主。 Go沒有內建操作資料庫的驅動,僅僅定義了一套database/sql介面,使用者開發時需要基於驅動介面開發相

循序漸進學.Net Core Web Api開發系列【8】訪問資料庫(基本功能)

系列目錄 一、概述 本篇討論如何連線資料庫,包括連線SQL Server 和 連線MySQL,然後做一些基本的資料操作。 二、連線SQL Server 首先通過NuGet新增相關的包: 新建一個實體類: public class Product {

Go Web 程式設計資料庫

概述 資料庫用來儲存資料。只要不是玩具專案,每個專案都需要用到資料庫。現在用的最多的還是 MySQL,PostgreSQL的使用也在快速增長中。 在 Web 開發中,資料庫也是必須的。本文將介紹如何在 Go 語言中操作資料庫,基於 MySQL。本文假定大家已經掌握了資料庫和 MySQL 的基礎知識。 關於 M

基於gin的golang web開發訪問mysql資料庫

web開發基本都離不開訪問資料庫,在Gin中使用mysql資料庫需要依賴mysql的驅動。直接使用驅動提供的API就要寫很多樣板程式碼。你可以找到很多擴充套件包這裡介紹的是jmoiron/sqlx。另外還有一個用來處理空值的包guregu/null。 ``` go get github.com/go-sql

十五、Go基礎程式設計複合型別—陣列

概述 陣列是指一系列同一型別資料的集合。陣列中包含的每個資料被稱為陣列元素(element),一個數組包含的元素個數被稱為陣列的長度。 陣列⻓度必須是常量,且是型別的組成部分。 [2]int 和 [3]int 是不同型別 var n int = 10 var a [n]i

十四、Go基礎程式設計複合型別—指標

指標是一個代表著某個記憶體地址的值。這個記憶體地址往往是在記憶體中儲存的另一個變數的值的起始位置。Go語言對指標的支援介於Java語言和C/C++語言之間,它既沒有想Java語言那樣取消了程式碼對指標的直接操作的能力,也避免了C/C++語言中由於對指標的濫用而造成的安全和可靠性問題。  

十三、Go基礎程式設計工程管理

概述 在實際的開發工作中,直接呼叫編譯器進行編譯和連結的場景是少而又少,因為在工程中不  會簡單到只有一個原始碼檔案,且原始檔之間會有相互的依賴關係。如果這樣一個檔案一個檔案逐步編譯,那不亞於一場災難。 Go語言的設計者作為行業老將,自然不會忽略這一點。早期Go語言使用makefile

十二、Go基礎程式設計工作區

工作區介紹 Go程式碼必須放在工作區中。工作區其實就是一個對應於特定工程的目錄,它應包含3個子目錄:src目錄、pkg目錄和bin目錄。 src目錄:用於以程式碼包的形式組織並儲存Go原始碼檔案。(比如:.go .c .h .s等) pkg目錄:用於存放經由go install命令構建安裝

十一、Go基礎程式設計遞迴函式、函式型別、匿名函式與閉包

1. 遞迴函式 遞迴指函式可以直接或間接的呼叫自身。 遞迴函式通常有相同的結構:一個跳出條件和一個遞迴體。所謂跳出條件就是根據傳入的引數判斷是否需要停止遞迴,而遞迴體則是函式自身所做的一些處理。 //通過迴圈實現1+2+3……+100 func Test01() int { i

Go基礎程式設計Go語言介紹

Go語言是什麼 2009年11月10日,Go語言正式成為開源程式語言家庭的一員。 Go語言(或稱Golang)是雲端計算時代的C語言。Go語言的誕生是為了讓程式設計師有更高的生產效率,Go語言專門針對多處理器系統應用程式的程式設計進行了優化,使用Go編譯的程式可以媲美C或C++程式碼的速度,

十八、Go基礎程式設計複合型別—結構體

1 結構體型別 有時我們需要將不同型別的資料組合成一個有機的整體,如:一個學生有學號/姓名/性別/年齡/地址等屬性。顯然單獨定義以上變數比較繁瑣,資料不便於管理。 結構體是一種聚合的資料型別,它是由一系列具有相同型別或不同型別的資料構成的資料集合。每個資料稱為結構體的

二十、Go基礎程式設計正則表示式

正則表示式是一種進行模式匹配和文字操縱的複雜而又強大的工具。雖然正則表示式比純粹的文字匹配效率低,但是它卻更靈活。按照它的語法規則,隨需構造出的匹配模式就能夠從原始文字中篩選出幾乎任何你想要得到的字元組合。 Go語言通過regexp標準包為正則表示式提供了官方支援,如果你已

二十二、Go基礎程式設計併發程式設計—goroutine

1 goroutine是什麼 goroutine是Go並行設計的核心。goroutine說到底其實就是協程,但是它比執行緒更小,十幾個goroutine可能體現在底層就是五六個執行緒,Go語言內部幫你實現了這些goroutine之間的記憶體共享。執行goroutine只需極

二十二、Go基礎程式設計併發程式設計—channel

goroutine執行在相同的地址空間,因此訪問共享記憶體必須做好同步。goroutine 奉行通過通訊來共享記憶體,而不是共享記憶體來通訊。 引⽤型別 channel 是 CSP 模式的具體實現,用於多個 goroutine 通訊。其內部實現了同步,確保併發安全。

Go Web程式設計一: Go Web 基礎

Go Web 基礎概念與程式碼閱讀 1. Go 搭建簡單的web 服務 Go 語言裡面提供了一個完善的 net/http 包,通過http 包可以很方便的就搭建起來一個可以執行的Web服務。同時使用這個包能很簡單地對Web的路由,靜態檔案,模版,

Go基礎程式設計複合型別—陣列

概述 陣列是指一系列同一型別資料的集合。陣列中包含的每個資料被稱為陣列元素(element),一個數組包含的元素個數被稱為陣列的長度。 陣列⻓度必須是常量,且是型別的組成部分。 [2]int 和 [3]int 是不同型別。 var n int

Go Web程式設計 第三章--接收請求

net/http標準庫 net/http標準庫通常包括兩個部分,客戶端和伺服器,我們可以通過ListenAndServe建立一個簡陋的伺服器 package main import ( "net/http" ) func main() { http.ListenAndServe("

Go Web程式設計 第四章--處理請求

請求和響應 Request結構 URL欄位 Header欄位 Body欄位 Form, PostForm, MultipartForm欄位 在處理器函式中,我們可以通過Request獲取各個欄位的詳細資訊 func get_request_value(w *http.Respon

Go基礎程式設計獲取命令列引數

package main import ( "fmt" "os" //os.Args所需的包 ) func main() { args := os.Args //獲取使用者輸入的所有引數 //如果使用者沒有輸入

Go基礎程式設計格式化輸出、型別轉換、類型別名

使用fmt包來格式化字串 fmt.Printf()格式字串: 列印格式 含義 %% 一個%字面量 %b 一個二進位制整數值(基數為2),或者是一個(高階的)用