Go Web 程式設計入門--應用 ORM

KevinYan發表於2020-02-16

Go Web 程式設計--應用 ORM

上篇文章中我們在使用的開發環境中增加了MySQL容器,然後介紹了使用database/sql標準庫結合資料庫驅動包進行資料庫操作的方法。不過它們是相對偏底層的軟體包。實際開發經常會使用一些在它的基礎上封裝的 ORM庫。ORM的查詢使用起來更簡單些,語法更富表達力。這篇文章我們主要探究下面這些內容。

  • gorm的基本用法
  • 如何管理ORM的使用
  • 如何合理規劃專案目錄結構

安裝gorm包

gorm是一個出色的,對開發人員友好的 Golang ORM 庫,其支援的特性包括:

  • 全特性 ORM (幾乎包含所有特性)
  • 模型關聯 (一對一, 一對多,一對多(反向), 多對多, 多型關聯)
  • 鉤子 (Before/After Create/Save/Update/Delete/Find)
  • 預載入
  • 事務
  • 複合主鍵
  • SQL 構造器
  • 自動遷移
  • 日誌

使用如下命令進行安裝:

go get -u github.com/jinzhu/gorm

將gorm加入專案中

規劃資料模型目錄結構

我們在專案根目錄下新建如下目錄:

http_demo
|
└───model
│   └───dao
│   │   init.go
│   └───────table
│           │   user.go

在 Go 中包以目錄的形式來組織,所以model包中存放所有資料模型,dao代表資料訪問物件,存放資料庫CRUD方法的封裝,其中的init.go存放dao包的初始化函式主要是用來在載入包後連線上資料庫。table包裡放與資料表對應的模型定義(使用 ORM 之前要先定義模型與資料庫中的表對應),在示例裡我們會定義一個User模型放在user.go檔案中。

規劃完目錄後就可以在各部分寫相應的程式碼了,首先來看使用gorm連線資料庫。

連線資料庫

我們在dao包的init.go中加入包的初始化邏輯進行資料庫連線,初始化函式會在dao包第一次被匯入時執行,由於gorm文件連線資料庫的例子太簡單,參考價值不大,我們根據專案需要做些簡單封裝,init.go中的程式碼如下所示:

package dao

import (
   _ "github.com/go-sql-driver/mysql"
   "github.com/jinzhu/gorm"
   "time"
)

var _DB *gorm.DB

func DB() *gorm.DB {
   return _DB
}

func init() {
   _DB = initDB()
}

func initDB() *gorm.DB {
   // In our docker dev environment use
   // db, err := gorm.Open("mysql", "go_web:go_web@tcp(database:3306)/go_web?charset=utf8&parseTime=True&loc=Local")
   db, err := gorm.Open("mysql", "go_web:go_web@tcp(localhost:33063)/go_web?charset=utf8&parseTime=True&loc=Local")
   if err != nil {
      panic(err)
   }
   db.DB().SetMaxOpenConns(100)
   db.DB().SetMaxIdleConns(10)
   db.DB().SetConnMaxLifetime(time.Second * 300)
   if err = db.DB().Ping(); err != nil {
      panic(err)
   }
   return db
}

程式碼很簡單,大家實操的時候根據自己的MySQL配置更改程式碼裡面的配置就行了。唯一說明一點的是,如果使用了我們提提供的Docker環境,在連線資料庫時host要改為database:3306,因為我在容器環境裡將MySQL容器的服務名定義成了database,在執行了Goapp容器需要用服務名訪問容器網路中的其他容器。關於容器環境的詳細配置請大家檢視Go Web程式設計–應用資料庫 中的描述。

定義模型

使用模型訪問資料庫的表之前我們需要先定義對應的模型。我們示例中現在只有一個users表,接下來我們在table包中新增users表的模型定義並放置在user.go檔案中。

package table

import "time"

type User struct {
   Id        int64     `gorm:"column:id;primary_key"`
   UserName  string    `gorm:"column:username"`
   Secret    string    `gorm:"column:secret;type:varchar(1000)"`
   CreatedAt time.Time `gorm:"column:created_at"`
   UpdatedAt time.Time `gorm:"column:updated_at"`
}

// TableName sets the insert table name for this struct type
func (m *User) TableName() string {
   return "users"
}

模型 CRUD

關於模型的 CRUD,建議將單個模型的CRUD放在dao包的單個檔案中,這樣方便以後程式碼的管理。這裡多說一點,建議不要直接用controller或者叫handler包直接訪問dao包,而是在中間加一層logic包,把邏輯放在這一層。這樣對程式碼的管理、複用性都有幫助。

因為資料庫的 CRUD 有很多種操作,本文的目的是幫助大家快速開始使用gorm所以我就只放簡單的 CRUD 做演示了。大家按照這裡步驟引入gorm後用到其他的資料庫操作了直接去官方文件裡查一查就好。

dao包中新建user.go用來存放User模型的操作方法。

package dao

import "example.com/http_demo/model/dao/table"

func CreateUser(user *table.User) (err error) {
    err = DB().Create(user).Error

    return
}

func GetUserById(userId int64) (user *table.User, err error) {
    user = new(table.User)
    err = DB().Where("id = ?", userId).First(user).Error

    return
}

func GetAllUser() (users []*table.User, err error) {
    err = DB().Find(&users).Error
    return
}

func UpdateUserNameById(userName string, userId int64) (err error) {
    user := new(table.User)
    err = DB().Where("id = ?", userId).First(user).Error
    if err != nil {
        return
    }

    user.UserName = userName
    err = DB().Save(user).Error

    return
}

func DeleteUserById(userId int64) (err error) {
    user := new(table.User)
    err = DB().Where("id = ?", userId).First(user).Error
    if err != nil {
        return
    }
    err = DB().Delete(user).Error

    return
}

驗證ORM 方法

經過上面幾步的設定後我們就可以在專案裡使用gorm訪問資料庫了,由於我們專案的main goroutine中執行了http服務,所以我們使用測試用例對上面dao包中定義的幾個方法進行一下測試。

篇幅原因我就只貼一個GetAllUsers方法的測試用例了:

func TestGetAllUser(t *testing.T) {
    tests := []struct {
        name      string
        wantErr   bool
    }{
        {
            name: "test",
            wantErr: false,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            gotUsers, err := GetAllUser()
            if (err != nil) != tt.wantErr {
                t.Errorf("GetAllUser() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            for _, user := range gotUsers {
                log.Info("user: %v", user)
            }

        })
    }
}

執行測試後,可以看到結果:

INFO user: &{1   2020-02-15 14:14:46 +0800 CST 2020-02-15 06:44:17 +0800 CST}
--- PASS: TestGetAllUsers (0.00s)
    --- PASS: TestGetAllUsers/test (0.00s)
PASS

Process finished with exit code 0

關注文末的公眾號回覆gohttp05可以得到完整的測試用例程式碼,建議這些CRUD都要寫好測試用例進行自測,使用GoLand可以很容易的生成測試函式和執行測試。

重新規劃專案目錄

引入ORM後,我們專案中的程式碼就比較多了,都放在根目錄下的main包中有點雜亂,所以我們根據各部分的功能和職責對專案目錄進行了簡單的劃分,劃分後的目錄結構如下:

http_demo
|
└───handler//route handler
|
└───logic//business logic
|
└───middleware
│
└───model
│   └───dao
│   │   init.go
│   └───table
│       │   user.go
└───router// router
|
|   main.go

感覺今天的內容還是挺多的,尤其對於剛入門Go的同學們一定要把今天的程式碼下載下來實操一遍才能掌握。gorm提供的功能還是很多的,每個功能在官方文件裡都有講解,我們這裡就不做過多介紹了。這篇文章的目的主要是讓大家能快速入門,同時把ORM相關的程式碼管理和初始化流程做的規範些,這些組織方式完全可以應用到生產級別的專案中的。

關注文末的公眾號回覆gohttp05獲取文章中完整的原始碼,喜歡我的文章請點贊和收藏。

前文回顧

深入學習用Go編寫HTTP伺服器

Web伺服器路由

用Docker快速搭建Go開發環境

十分鐘學會用Go編寫Web中介軟體

Go Web程式設計–應用資料庫

Go Web程式設計--應用ORM

本作品採用《CC 協議》,轉載必須註明作者和本文連結
公眾號:網管叨bi叨 | Golang、Laravel、Docker、K8s等學習經驗分享

相關文章