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
,在運行了 Go
的 app
容器需要用服務名訪問容器網絡中的其他容器。關於容器環境的詳細配置請大家查看 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
獲取文章中完整的源代碼,喜歡我的文章請點贊和收藏。
前文回顧