首頁 > 軟體

Golang連線並操作PostgreSQL資料庫基本操作

2022-09-16 22:03:29

前言:

本篇文章對如何使用golang連線並操作postgre資料庫進行了簡要說明。文中使用到的主要工具:DBeaver21、VSCode,Golang1.17。

以使用者,文章,評論三個表作為例子,下面是資料庫建表sql:

CREATE TABLE public.user_info (
    u_id serial4 NOT NULL,
    user_name varchar NULL,
    create_time date NULL,
    CONSTRAINT user_info_pk PRIMARY KEY (u_id)
);
CREATE TABLE public.user_info (
    u_id serial4 NOT NULL,
    user_name varchar NULL,
    create_time date NULL,
    CONSTRAINT user_info_pk PRIMARY KEY (u_id)
);
CREATE TABLE public."comment" (
    c_id serial4 NOT NULL,
    "content" varchar NULL,
    CONSTRAINT comment_pk PRIMARY KEY (c_id)
);

連線資料庫

連線postgre資料庫的驅動有很多,我們選用了github.com/lib/pq。下面看連線的方法。我們引入pq包時使用了_進行匿名載入,而不是直接使用驅動包。在對資料庫的操作仍然是使用自帶的sql包。另外,postgre預設使用的是public模式(schema),我們建立的表也是在這個模式下的。可以直接在資料庫中修改預設模式或者在連線url中新增currentSchema=myschema來指定預設的模式,當然也可以在sql中使用myschema.TABLE來指定要存取的模式。

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/lib/pq"
)

var db *sql.DB

func DbOpen() {
    var err error
    //引數根據自己的資料庫進行修改
    db, err = sql.Open("postgres", "host=localhost port=5432 user=angelhand password=2222 dbname=ahdb sslmode=disable")
    checkError(err)
    err = db.Ping()
    checkError(err)
}

sql.DB

需要注意的是,sql.DB並不是資料庫連線,而是一個go中的一個資料結構:

type DB struct {
    // Atomic access only. At top of struct to prevent mis-alignment
    // on 32-bit platforms. Of type time.Duration.
    waitDuration int64 // Total time waited for new connections.

    connector driver.Connector
    // numClosed is an atomic counter which represents a total number of
    // closed connections. Stmt.openStmt checks it before cleaning closed
    // connections in Stmt.css.
    numClosed uint64

    mu           sync.Mutex // protects following fields
    freeConn     []*driverConn
    connRequests map[uint64]chan connRequest
    nextRequest  uint64 // Next key to use in connRequests.
    numOpen      int    // number of opened and pending open connections
    // Used to signal the need for new connections
    // a goroutine running connectionOpener() reads on this chan and
    // maybeOpenNewConnections sends on the chan (one send per needed connection)
    // It is closed during db.Close(). The close tells the connectionOpener
    // goroutine to exit.
    openerCh          chan struct{}
    closed            bool
    dep               map[finalCloser]depSet
    lastPut           map[*driverConn]string // stacktrace of last conn's put; debug only
    maxIdleCount      int                    // zero means defaultMaxIdleConns; negative means 0
    maxOpen           int                    // <= 0 means unlimited
    maxLifetime       time.Duration          // maximum amount of time a connection may be reused
    maxIdleTime       time.Duration          // maximum amount of time a connection may be idle before being closed
    cleanerCh         chan struct{}
    waitCount         int64 // Total number of connections waited for.
    maxIdleClosed     int64 // Total number of connections closed due to idle count.
    maxIdleTimeClosed int64 // Total number of connections closed due to idle time.
    maxLifetimeClosed int64 // Total number of connections closed due to max connection lifetime limit.

    stop func() // stop cancels the connection opener.
}

在拿到sql.DB時並不會建立新的連線,而可以認為是拿到了一個資料庫連線池,只有在執行資料庫操作(如Ping()操作)時才會自動生成一個連線並連線資料庫。在連線操作執行完畢後應該及時地釋放。此處說的釋放是指釋放連線而不是sql.DB連線,通常來說一個sql.DB應該像全域性變數一樣長期儲存,而不要在某一個小函數中都進行Open()Close()操作,否則會引起資源耗盡的問題。

增刪改查

下面程式碼實現對資料簡單的增刪改查操作。

插入資料

func insert() {
    stmt, err := db.Prepare("INSERT INTO user_info(user_name,create_time) VALUES($1,$2)")
    if err != nil {
        panic(err)
    }

    res, err := stmt.Exec("ah", time.Now())
    if err != nil {
        panic(err)
    }

    fmt.Printf("res = %d", res)
}

使用Exec()函數後會返回一個sql.Result即上面的res變數接收到的返回值,它提供了LastInserId() (int64, error)RowsAffected() (int64, error)分別獲取執行語句返回的對應的id和語句執行所影響的行數。

更新資料

func update() {
    stmt, err := db.Prepare("update user_info set user_name=$1 WHERE u_id=$2")
    if err != nil {
        panic(err)
    }
    res, err := stmt.Exec("angelhand", 1)
    if err != nil {
        panic(err)
    }

    fmt.Printf("res = %d", res)
}

查詢資料

結構體如下:

type u struct {
    id          int
    user_name   string
    create_time time.Time
}

接下來是查詢的程式碼

func query() {
    rows, err := db.Query("select u_id, user_name, create_time from user_info where user_name=$1", "ah")
    if err != nil {
        panic(err)

    }
    //延遲關閉rows
    defer rows.Close()

    for rows.Next() {
        user := u{}
        err := rows.Scan(&user.id, &user.user_name, &user.create_time)
        if err != nil {
            panic(err)
        }
        fmt.Printf("id = %v, name = %v, time = %vn", user.id, user.user_name, user.create_time)
    }
}

可以看到使用到的幾個關鍵函數rows.Close()rows.Next()rows.Scan()。其中rows.Next()用來遍歷從資料庫中獲取到的結果集,隨用用rows.Scan()來將每一列結果賦給我們的結構體。

需要強調的是rows.Close()。每一個開啟的rows都會佔用系統資源,如果不能及時的釋放那麼會耗盡系統資源。defer語句類似於java中的finally,功能就是在函數結束前執行後邊的語句。換句話說,在函數結束前不會執行後邊的語句,因此在耗時長的函數中不建議使用這種方式釋放rows連線。如果要在迴圈中重發查詢和使用結果集,那麼應該在處理完結果後顯式呼叫rows.Close()

db.Query()實際上等於建立db.Prepare(),執行並關閉之三步操作。

還可以這樣來查詢單條記錄:

err := db.Query("select u_id, user_name, create_time from user_info where user_name=$1", "ah").Scan(&user.user_name)

刪除資料

func delete() {
    stmt, err := db.Prepare("delete from user_info where user_name=$1")
    if err != nil {
        panic(err)
    }
    res, err := stmt.Exec("angelhand")
    if err != nil {
        panic(err)
    }

    fmt.Printf("res = %d", res)
}

總結

到此這篇關於Golang連線並操作PostgreSQL資料庫基本操作的文章就介紹到這了,更多相關Golang連線操作PostgreSQL內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com