49. 訪問PostgreSQL數(shù)據(jù)庫增刪改查

PostgreSQL是常見的免費(fèi)的大型關(guān)系型數(shù)據(jù)庫欧穴,具有豐富的數(shù)據(jù)類型鸽捻,也是軟件項目常用的數(shù)據(jù)庫之一说榆。
因其可靠的穩(wěn)定性渔隶,通常我們可以拿它來做Oracle的替代品。
使用 Go 語言訪問 PostgreSQL 數(shù)據(jù)庫纵顾,與其他數(shù)據(jù)庫是略有不同的伍茄。
為了能夠?qū)?shù)據(jù)庫進(jìn)行訪問,我們先建立一個數(shù)據(jù)庫 cofoxdb施逾,并且建立一個數(shù)據(jù)表 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 '最新登錄時間(當(dāng)前登錄時間)';
COMMENT ON COLUMN "user".bak is '備注';
COMMENT ON COLUMN "user".online is '當(dāng)前在線敷矫,Y/N Y:在線 N:不在線';
COMMENT ON COLUMN "user"."createTime" is '記錄創(chuàng)建時間';
COMMENT ON COLUMN "user".creator is '記錄創(chuàng)建人';
COMMENT ON COLUMN "user"."updateTime" is '記錄修改時間';
COMMENT ON COLUMN "user".updator is '記錄修改人';

在用 Go 語言編寫 PostgreSQL 的訪問代碼前,我們需要先了解一下 PostgreSQL 的一些特性音念。
PostgreSQL 無論是表名還是字段名沪饺,如果你需要使用大寫字母或者一些關(guān)鍵字,那么這個表名或字段在使用的時候闷愤,需要加 雙引號整葡。
在代碼中,也需要先添加數(shù)據(jù)庫驅(qū)動包讥脐。
我們推薦兩個包遭居,任意加一個就可以了。
_ "github.com/bmizerany/pq"
_ "github.com/lib/pq"
你會看到我的代碼中 import 塊是這樣的

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

其中一個驅(qū)動包是被注釋掉的旬渠。無論保留哪個俱萍,都不會影響代碼的運(yùn)行。只是不同的驅(qū)動包告丢,出現(xiàn)訪問錯誤時枪蘑,返回的信息內(nèi)容與格式不同♂猓看你喜歡用哪個了岳颇。
依然是先聲明全局變量 db 和 err ,再加載數(shù)據(jù)庫連接颅湘,并設(shè)定好連接池话侧。

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())
}

增刪改查四種操作,我們用四個函數(shù)來分別寫出來闯参。在主函數(shù) main 中就直接是這四個函數(shù)的調(diào)用了瞻鹏。

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

依然是你用哪個就寫哪個悲立,不用的就先注釋掉。
首先是插入數(shù)據(jù)新博。前面已經(jīng)說過了薪夕,要加雙引號的事情,這里就看一下 Go 語言里使用轉(zhuǎn)義符號在 SQL 里的用法吧叭披。

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

\" 就是 雙引號的轉(zhuǎn)義格式寥殖。雙引號成對出現(xiàn)玩讳,需要兩個這樣的符號涩蜘。\"user\"
這里還需要注意 VALUES 后面的參數(shù),是用 $ 符號跟著一個順序的數(shù)字的格式熏纯。這表明是第一個參數(shù)同诫、第二個參數(shù)、第三個...
這幾個參數(shù)對應(yīng)的就是下面的這行代碼樟澜,順序?qū)?yīng)

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

完整的 insert 函數(shù)代碼如下

//插入數(shù)據(jù)
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()

}

提交到數(shù)據(jù)庫的方法误窖,使用的是 RowsAffected 而不是我們常見的 LastInsertId 。這是 PostgreSQL 的特性決定的秩贰,我們無法獲取剛增加進(jìn)去的數(shù)據(jù)記錄的 id 號霹俺。(盡管在數(shù)據(jù)庫上,我們使用 serial 和 Primary Key 能夠讓記錄自增)
如果看過 “訪問MySQL數(shù)據(jù)庫增刪改查” 的那兩篇文章毒费,下面的修改函數(shù) update 就不難理解了丙唧。

//修改數(shù)據(jù)
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()
}

只是同樣要注意 \"的使用。
刪除函數(shù)也沒什么特殊之處觅玻,依然是 Exec(1)里面放的是參數(shù)值想际。
看刪除函數(shù) remove 的代碼

//刪除數(shù)據(jù)
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()

}

查詢數(shù)據(jù) query2() 函數(shù)。因為數(shù)據(jù)庫中很多字段是允許為 NULL 的溪厘,所以 query2 中很多變量的類型都是 sql.NullString胡本。
取得了返回數(shù)據(jù)記錄后,用 for rows.Next()遍歷所有的記錄畸悬,在循環(huán)中展示數(shù)據(jù)侧甫。

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括號中的參數(shù)順序,和 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()
}

//查詢數(shù)據(jù)
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括號中的參數(shù)順序披粟,和 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()
}

//插入數(shù)據(jù)
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()

}

//修改數(shù)據(jù)
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()
}

//刪除數(shù)據(jù)
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)
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末妆档,一起剝皮案震驚了整個濱河市僻爽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贾惦,老刑警劉巖胸梆,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敦捧,死亡現(xiàn)場離奇詭異,居然都是意外死亡碰镜,警方通過查閱死者的電腦和手機(jī)兢卵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绪颖,“玉大人秽荤,你說我怎么就攤上這事∧幔” “怎么了窃款?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長牍氛。 經(jīng)常有香客問我晨继,道長,這世上最難降的妖魔是什么搬俊? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任紊扬,我火速辦了婚禮,結(jié)果婚禮上唉擂,老公的妹妹穿的比我還像新娘餐屎。我一直安慰自己,他們只是感情好玩祟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布腹缩。 她就那樣靜靜地躺著,像睡著了一般卵凑。 火紅的嫁衣襯著肌膚如雪庆聘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天勺卢,我揣著相機(jī)與錄音伙判,去河邊找鬼。 笑死黑忱,一個胖子當(dāng)著我的面吹牛宴抚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播甫煞,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼菇曲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了抚吠?” 一聲冷哼從身側(cè)響起常潮,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎楷力,沒想到半個月后喊式,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孵户,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年岔留,在試婚紗的時候發(fā)現(xiàn)自己被綠了夏哭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡献联,死狀恐怖竖配,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情里逆,我是刑警寧澤进胯,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站运悲,受9級特大地震影響龄减,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜班眯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望烁巫。 院中可真熱鬧署隘,春花似錦、人聲如沸亚隙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽阿弃。三九已至诊霹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渣淳,已是汗流浹背脾还。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留入愧,地道東北人鄙漏。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像棺蛛,于是被迫代替她去往敵國和親怔蚌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容