Go Web編程--應用數據庫


今天咱們繼續接着前幾篇關於Go Web編程的文章往下延伸。在Web應用程序中幾乎每一個應用場景都須要存儲和檢索數據庫中的數據。當你處理動態內容,爲用戶提供表單以輸入數據或存儲登陸名和密碼憑據以供用戶進行身份驗證時,都須要用到數據庫。 MySQL數據庫是整個互聯網中最經常使用的數據庫。MySQL已經存在了很長時間,還在不停的進化而且隨着互聯網一塊兒發展已屢次證實了其位置和穩定性。html

本文咱們將探究Go中數據庫訪問的基礎知識,在開始以前咱們先更新一下咱們使用的開發環境,以前在文章用Docker快速搭建Go開發環境 中咱們只應用了一個運行 go的容器,如今咱們爲開發環境加上數據庫。mysql

關注文末公衆號,回覆 gohttp04 獲取容器編排文件和文章中用到的源代碼。

在開發環境中增長MySQL容器

打開咱們以前編寫的docker-compose.yml文件,添加以下配置:git

version: '3'
services:
  ......
  # The Database
  database:
    image: mysql:5.7
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      - "MYSQL_DATABASE=go_web"
      - "MYSQL_USER=go_web"
      - "MYSQL_PASSWORD=go_web"
      - "MYSQL_ROOT_PASSWORD=secret"
    ports:
      - "33063:3306"

volumes:
  dbdata:
  • 由於容器退出時會銷燬容器內的全部文件,因此對於MySQL這種存儲持久化數據的容器須要與外部宿主機作文件映射,這樣再次啓動MySQL容器後就會從數據映射中讀取以前的數據。
  • 在編排文件的中咱們經過 volumes 命令建立了一個名爲 dbdata 的數據卷(dbdata 後面的冒號是有意寫上去的,這是 YML 文件的一個語法限制,不用太關心)
  • 定義完數據卷後,在上面咱們使用 dbdata:/var/lib/mysql 的格式,通知 Docker,將 dbdata 數據卷掛在到容器中的 /var/lib/mysql 目錄上。
  • environments 中設置的是 MySQL 容器須要的四個必要參數。
  • ports 端口映射中,咱們將本地電腦的 33063 端口映射到容器的 3306 端口,這樣咱們就能經過電腦上的數據庫工具鏈接到 容器內的MySQL 了。

添加完MySQL後完整的編排文件就變成:程序員

version: '3'
services:
  # The Application
  app:
    image: golang:latest
    working_dir: /go/src/example.com/http_demo
    volumes:
      - /$GOPATH/src/example.com/http_demo:/go/src/code.qschou.com/http_demo
      - /$GOPATH/src:/go/src
    ports:
      - "8000:8080"
    environment:
      WORKING_DIR: /go/src/e.qschou.com/practice/http_demo
    command: go run /go/src/example.com/http_demo/main.go

  # The Database
  database:
    image: mysql:5.7
    volumes:
      - dbdata:/var/lib/mysql
    environment:
      - "MYSQL_DATABASE=go_web"
      - "MYSQL_USER=go_web"
      - "MYSQL_PASSWORD=go_web"
      - "MYSQL_ROOT_PASSWORD=secret"
    ports:
      - "33063:3306"

volumes:
  dbdata:

能夠公衆號內發送gohttp04獲取Docker編排文件和文章中用到的源代碼。github

安裝go-sql-driver/mysql

Go語言標準庫中database / sql包,用於查詢各類SQL數據庫。它將全部通用SQL功能抽象到一個API中供開發者使用。 可是Go的標準庫中不包括數據庫驅動程序。數據庫驅動程序由特定軟件包提供的,用於實現特定數據庫底層的封裝。這對於向前兼容頗有用,也使得Go不會變的臃腫。由於在建立全部Go軟件包時,開發人員沒法預見將來會有什麼數據庫會被投入使用,並且要支持每一個可能的數據庫將須要進行大量維護工做。golang

使用下面命令安裝MySQL驅動包:web

go get -u github.com/go-sql-driver/mysql

鏈接MySQL數據庫

要檢查咱們是否能夠鏈接到數據庫,咱們須要導入database/sqlgo-sql-driver/mysql兩個包,並鏈接數據庫:sql

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

func MysqlDemoCode() {

    db, err := sql.Open("mysql", "go_web:go_web@tcp(127.0.0.1:3306)/go_web")
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    if err := db.Ping(); err != nil {
        log.Fatal(err)
    }
}
  • 你的代碼應僅引用在database/sql中定義的類型和函數。這有助於避免使代碼依賴於特定驅動程序,從而使你能夠經過最少的代碼更改來更改使用的數據庫驅動(相應也會更改使用的數據庫類型)。
  • 代碼中對驅動包使用匿名包導入,將go-sql-driver/mysql的包別名設置爲_,這樣驅動程序導出的名稱對咱們的代碼都是不可見的,可是在幕後go-sql-driver/mysql將自身註冊爲可用於database/sql的驅動包。通常而言,除了運行包的init函數外,不會發生任何其餘事情。
  • sql.Open()不會創建與數據庫的任何鏈接,也不會驗證驅動程序的鏈接參數。它只是返回抽象數據庫的對象以供後面使用。數據庫鏈接在真正須要訪問數據庫的時候纔會創建。

咱們能夠經過單元測試驗證數據庫是否能正確鏈接上,測試代碼我就不貼了,能夠經過文章的源碼包裏看到,惟一提醒一點,若是在本地機器裏運行測試須要把上面sql.Open()配置的端口改成33063docker

建立表

咱們接下來建立一個像這樣的表:數據庫

id username password created_at
1 Joshua secret 2020-02-13 12:30:00

使用 database/sql包執行建表語句就能夠在咱們的 MySQL數據庫中建立表:

query := `
   CREATE TABLE users (
       id INT AUTO_INCREMENT,
       username TEXT NOT NULL,
       password TEXT NOT NULL,
       created_at DATETIME,
       PRIMARY KEY (id)
   );`
// 執行後必定要檢查err
_, err := db.Exec(query)

插入新數據

默認狀況下,Go使用準備好的語句(prepare)將動態數據插入到咱們的SQL語句中,這是一種將用戶提供的數據安全地傳遞到咱們的數據庫而不會形成任何損壞的方式。在Web編程的早期,程序員將數據和查詢直接傳遞給數據庫,這致使了巨大的漏洞,並可能破壞整個Web應用程序。

要將咱們的第一個用戶插入數據庫表,咱們將建立一個以下的SQL查詢。語句中的問號告訴SQL驅動程序,它們是實際數據的佔位符。下面你能夠看到咱們討論的準備好的語句:

username := "johndoe"
password := "secret"
createdAt := time.Now()

result, err := db.Exec(`INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)`, username, password, createdAt)

結果包含最後插入的ID(自增ID)的信息以及此查詢影響的行數。

// 獲取新插入數據庫的用戶ID
userID, err := result.LastInsertId()

查詢表數據

如今咱們的表中有一個用戶,咱們想要查詢它並獲取其全部信息。使用database/sql包咱們有兩種查詢表的方式。db.Query能夠查詢多行,以便咱們進行迭代;db.QueryRow查詢特定的行。

查詢單行

咱們首先聲明一些變量來存儲數據,而後查詢單個數據庫行:

var (
    id        int
    username  string
    password  string
    createdAt time.Time
)

query := `SELECT id, username, password, created_at FROM users WHERE id = ?`
err := db.QueryRow(query, 1).Scan(&id, &username, &password, &createdAt)

查詢多行

上面咱們演示瞭如何查詢單個用戶行, 接下來演示下如何查詢多個數據行並將數據存儲到結構體切片中:

type user struct {
    id        int
    username  string
    password  string
    createdAt time.Time
}

rows, err := db.Query(`SELECT id, username, password, created_at FROM users`) //檢查錯誤
defer rows.Close()

var users []user
for rows.Next() {
    var u user
    err := rows.Scan(&u.id, &u.username, &u.password, &u.createdAt) 
    users = append(users, u)
}
err := rows.Err() //檢查錯誤

篇幅緣由代碼中全部的錯誤檢查都被故意忽略了,在實際使用中必定要記得作錯誤檢查。

users切片中存儲的數據相似這樣:

users {
    user {
        id:        1,
        username:  "Joshua",
        password:  "secret",
        createdAt: time.Time{wall: 0x0, ext: 63701044325, loc: (*time.Location)(nil)},
    },
    user {
        id:        2,
        username:  "alice",
        password:  "bob",
        createdAt: time.Time{wall: 0x0, ext: 63701044622, loc: (*time.Location)(nil)},
    },
}

刪除表數據

從咱們的表中刪除數據同建立表和插入數據同樣也是使用.Exec

result, err := db.Exec(`DELETE FROM users WHERE id = ?`, 1) // 記得檢查錯誤

後續

database/sql提供的MySQL查詢功能仍是很全面的使用的更多介紹能夠參考http://go-database-sql.org/in...,不過它是一個相對偏底層的庫。實際開發中每每會使用一些在它的基礎上封裝的ORM庫。ORM的查詢使用起來更簡單些,語法表達力更強也更方便於代碼管理。因此今天的文章主要是對database/sql作一下簡單介紹,入門便可,後續關於ORM庫的使用時再介紹更多查詢的使用方法。另外也在咱們的Docker環境中增長了 MySQL容器,你們也不要忘記更新。

公衆號回覆gohttp04獲取更新後的Docker編排文件和文章中用到的源代碼。

前文回顧

深刻學習用Go編寫HTTP服務器

Web服務器路由

用Docker快速搭建Go開發環境

十分鐘學會用Go編寫Web中間件

tWbHIMFsM3.png

相關文章
相關標籤/搜索