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, ®istTime, &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, ®istTime, &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)
}
}