1. 程式人生 > 其它 >49. 訪問PostgreSQL資料庫增刪改查 | 厚土Go學習筆記

49. 訪問PostgreSQL資料庫增刪改查 | 厚土Go學習筆記

PostgreSQL是常見的免費的大型關係型資料庫,具有豐富的資料型別,也是軟體專案常用的資料庫之一。 因其可靠的穩定性,通常我們可以拿它來做Oracle的替代品。

使用 Go 語言訪問 PostgreSQL 資料庫,與其他資料庫是略有不同的。

為了能夠對資料庫進行訪問,我們先建立一個數據庫 cofoxdb,並且建立一個數據表 user

建表 SQL 指令碼如下

---------------------------
---postgresql SQL by Junbo Jian
---------------------------
drop table if exists "user";

CREATE TABLE "user"(
    id serial PRIMARY KEY,
    "userName" varchar(45) UNIQUE,
    "password" varchar(255) NOT NULL,
    "nickName" varchar(45) UNIQUE,
    "registTime" time with time zone NOT NULL DEFAULT now(),
    "lastTimeLogin" time with time zone,
    "newLoginTime" time with time zone,
    bak varchar(1000) DEFAULT NULL,
    online char(1) DEFAULT 'N',
    "createTime" time with time zone NOT NULL,
    creator varchar(45) DEFAULT NULL,
    "updateTime" time with time zone DEFAULT NULL,
    updator varchar(45) DEFAULT NULL
    
);

COMMENT ON COLUMN "user".id is '流水號';
COMMENT ON COLUMN "user"."userName" is '使用者名稱【不可更改】';
COMMENT ON COLUMN "user"."password" is '密碼';
COMMENT ON COLUMN "user"."nickName" is '暱稱';
COMMENT ON COLUMN "user"."registTime" is '使用者註冊時間';
COMMENT ON COLUMN "user"."lastTimeLogin" is '上次登入時間';
COMMENT ON COLUMN "user"."newLoginTime" is '最新登入時間(當前登入時間)';
COMMENT ON COLUMN "user".bak is '備註';
COMMENT ON COLUMN "user".online is '當前線上,Y/N Y:線上 N:不線上';
COMMENT ON COLUMN "user"."createTime" is '記錄建立時間';
COMMENT ON COLUMN "user".creator is '記錄建立人';
COMMENT ON COLUMN "user"."updateTime" is '記錄修改時間';
COMMENT ON COLUMN "user".updator is '記錄修改人';

在用 Go 語言編寫 PostgreSQL 的訪問程式碼前,我們需要先了解一下 PostgreSQL 的一些特性。

PostgreSQL 無論是表名還是欄位名,如果你需要使用大寫字母或者一些關鍵字,那麼這個表名或欄位在使用的時候,需要加 雙引號

在程式碼中,也需要先新增資料庫驅動包。

我們推薦兩個包,任意加一個就可以了。

_ "github.com/bmizerany/pq" _ "github.com/lib/pq"

你會看到我的程式碼中 import 塊是這樣的

import (
    "database/sql"
    "fmt"
    //_ "github.com/bmizerany/pq"
    _ "github.com/lib/pq"
    "time"
    "log"
)

其中一個驅動包是被註釋掉的。無論保留哪個,都不會影響程式碼的執行。只是不同的驅動包,出現訪問錯誤時,返回的資訊內容與格式不同。看你喜歡用哪個了。

依然是先宣告全域性變數 db 和 err ,再載入資料庫連線,並設定好連線池。

var db *sql.DB
var err error

func init()  {
    db, err = sql.Open("postgres", "user=cofox password=Q1w2e3r4 dbname=cofoxdb sslmode=disable")
    check(err)

    db.SetMaxOpenConns(2000)
    db.SetMaxIdleConns(1000)
    check(db.Ping())
}

增刪改查四種操作,我們用四個函式來分別寫出來。在主函式 main 中就直接是這四個函式的呼叫了。

func main() {
    //query2()
    insert()
    //update()
    //remove()
}

依然是你用哪個就寫哪個,不用的就先註釋掉。

首先是插入資料。前面已經說過了,要加雙引號的事情,這裡就看一下 Go 語言裡使用轉義符號在 SQL 裡的用法吧。

stmt, err := db.Prepare("INSERT INTO "user"("userName", password, "nickName", bak, creator)   VALUES ($1, $2, $3, $4, $5)")

" 就是 雙引號的轉義格式。雙引號成對出現,需要兩個這樣的符號。"user"

這裡還需要注意 VALUES 後面的引數,是用 $ 符號跟著一個順序的數字的格式。這表明是第一個引數、第二個引數、第三個...

這幾個引數對應的就是下面的這行程式碼,順序對應

res, err := stmt.Exec("cofox","123456","冷靜的狐狸","","gopher")

完整的 insert 函式程式碼如下

//插入資料
func insert()  {
    stmt, err := db.Prepare("INSERT INTO "user"("userName", password, "nickName", bak, creator)   VALUES ($1, $2, $3, $4, $5)")
    check(err)

    res, err := stmt.Exec("cofox","123456","冷靜的狐狸","","gopher")
    check(err)

    id, err := res.RowsAffected() //.LastInsertId()
    check(err)

    fmt.Println(id)
    stmt.Close()

}

提交到資料庫的方法,使用的是 RowsAffected 而不是我們常見的 LastInsertId 。這是 PostgreSQL 的特性決定的,我們無法獲取剛增加進去的資料記錄的 id 號。(儘管在資料庫上,我們使用 serial 和 Primary Key 能夠讓記錄自增)

如果看過 “訪問MySQL資料庫增刪改查” 的那兩篇文章,下面的修改函式update就不難理解了。

//修改資料
func update() {
    stmt, err := db.Prepare("UPDATE "user" SET password=$1, "nickName"=$2, "lastTimeLogin"=$3, "newLoginTime"=$4, bak=$5, online=$6, "updateTime"=$7, updator=$8  WHERE id=$9")
    check(err)

    res, err := stmt.Exec("7654321","厚土火焰山",time.Now().Format("2006-01-02 15:04:05"),time.Now().Format("2006-01-02 15:04:05"),"修改了一次","Y",time.Now().Format("2006-01-02 15:04:05"),"gopher", 1)
    check(err)

    num, err := res.RowsAffected()
    check(err)

    fmt.Println(num)
    stmt.Close()
}

只是同樣要注意 "的使用。

刪除函式也沒什麼特殊之處,依然是 Exec(1)裡面放的是引數值。

看刪除函式 remove 的程式碼

//刪除資料
func remove() {
    stmt, err := db.Prepare("DELETE FROM "user" WHERE id=$1")
    check(err)

    res, err := stmt.Exec(1)
    check(err)

    num, err := res.RowsAffected()
    check(err)

    fmt.Println(num)
    stmt.Close()

}

查詢資料 query2() 函式。因為資料庫中很多欄位是允許為 NULL 的,所以 query2 中很多變數的型別都是 sql.NullString

取得了返回資料記錄後,用 for rows.Next()遍歷所有的記錄,在迴圈中展示資料。

func query2()  {
    rows, err := db.Query("SELECT "id", "userName", "password", "nickName", "registTime", "lastTimeLogin", "newLoginTime", bak, online, "createTime", creator, "updateTime", updator FROM "user"")
    check(err)

    for rows.Next(){
        var id int
        var userName string
        var password string
        var nickName string
        var registTime string
        var lastTimeLogin sql.NullString
        var newLoginTime sql.NullString
        var bak sql.NullString
        var online sql.NullString
        var createTime sql.NullString
        var creator sql.NullString
        var updateTime sql.NullString
        var updator sql.NullString

        //注意這裡的Scan括號中的引數順序,和 SELECT 的欄位順序要保持一致。
        if err := rows.Scan(&id, &userName, &password, &nickName, &registTime, &lastTimeLogin, &newLoginTime, &bak, &online, &createTime, &creator, &updateTime, &updator); err != nil {
            log.Fatal(err)
        }

        fmt.Printf("id = "%d", userName = "%s", password = "%s", nickName = "%s", registTime = "%s", lastTimeLogin = "%s", newLoginTime = "%s", bak = "%s", online = "%s", createTime = "%s", creator = "%s", updateTime = "%s", updator = "%s"n",id, userName, password, nickName, registTime, lastTimeLogin.String, newLoginTime.String, bak.String, online.String, createTime.String, creator.String, updateTime.String, updator.String)

    }

    if err := rows.Err(); err != nil {
        log.Fatal(err)
    }
    rows.Close()
}

看一遍完整的 go 訪問 PostgreSQL 的程式碼

package main

import (
    "database/sql"
    "fmt"
    //_ "github.com/bmizerany/pq"
    _ "github.com/lib/pq"
    "time"
    "log"
)

var db *sql.DB
var err error

func init()  {
    db, err = sql.Open("postgres", "user=cofox password=Q1w2e3r4 dbname=cofoxdb sslmode=disable")
    check(err)

    db.SetMaxOpenConns(2000)
    db.SetMaxIdleConns(1000)
    check(db.Ping())
}

func main() {
    query2()
    //insert()
    //update()
    //remove()
}

//查詢資料
func query2()  {
    rows, err := db.Query("SELECT "id", "userName", "password", "nickName", "registTime", "lastTimeLogin", "newLoginTime", bak, online, "createTime", creator, "updateTime", updator FROM "user"")
    check(err)

    for rows.Next(){
        var id int
        var userName string
        var password string
        var nickName string
        var registTime string
        var lastTimeLogin sql.NullString
        var newLoginTime sql.NullString
        var bak sql.NullString
        var online sql.NullString
        var createTime sql.NullString
        var creator sql.NullString
        var updateTime sql.NullString
        var updator sql.NullString

        //注意這裡的Scan括號中的引數順序,和 SELECT 的欄位順序要保持一致。
        if err := rows.Scan(&id, &userName, &password, &nickName, &registTime, &lastTimeLogin, &newLoginTime, &bak, &online, &createTime, &creator, &updateTime, &updator); err != nil {
            log.Fatal(err)
        }

        fmt.Printf("id = "%d", userName = "%s", password = "%s", nickName = "%s", registTime = "%s", lastTimeLogin = "%s", newLoginTime = "%s", bak = "%s", online = "%s", createTime = "%s", creator = "%s", updateTime = "%s", updator = "%s"n",id, userName, password, nickName, registTime, lastTimeLogin.String, newLoginTime.String, bak.String, online.String, createTime.String, creator.String, updateTime.String, updator.String)

    }

    if err := rows.Err(); err != nil {
        log.Fatal(err)
    }
    rows.Close()
}

//插入資料
func insert()  {
    stmt, err := db.Prepare("INSERT INTO "user"("userName", password, "nickName", bak, creator)   VALUES ($1, $2, $3, $4, $5)")
    check(err)

    res, err := stmt.Exec("cofox","123456","冷靜的狐狸","","gopher")
    check(err)

    id, err := res.RowsAffected()//.LastInsertId()//
    check(err)

    fmt.Println(id)
    stmt.Close()

}

//修改資料
func update() {
    stmt, err := db.Prepare("UPDATE "user" SET password=$1, "nickName"=$2, "lastTimeLogin"=$3, "newLoginTime"=$4, bak=$5, online=$6, "updateTime"=$7, updator=$8  WHERE id=$9")
    check(err)

    res, err := stmt.Exec("7654321","厚土火焰山",time.Now().Format("2006-01-02 15:04:05"),time.Now().Format("2006-01-02 15:04:05"),"修改了一次","Y",time.Now().Format("2006-01-02 15:04:05"),"gopher", 1)
    check(err)

    num, err := res.RowsAffected()
    check(err)

    fmt.Println(num)
    stmt.Close()
}

//刪除資料
func remove() {
    stmt, err := db.Prepare("DELETE FROM "user" WHERE id=$1")
    check(err)

    res, err := stmt.Exec(1)
    check(err)

    num, err := res.RowsAffected()
    check(err)

    fmt.Println(num)
    stmt.Close()

}
func check(err error) {
    if err != nil{
        fmt.Println(err)
        panic(err)
    }
}