摘要:經過上面幾步的設置後我們就可以在項目裏使用gorm訪問數據庫了,由於我們項目的main goroutine中運行了http服務,所以我們使用測試用例對上面dao包中定義的幾個方法進行一下測試。我們在dao包的init.go中加入包的初始化邏輯進行數據庫連接,初始化函數會在dao包第一次被導入時執行,由於gorm文檔連接數據庫的例子太簡單,參考價值不大,我們根據項目需要做些簡單封裝,init.go中的代碼如下所示:。

這篇文章我們主要探究下面這些內容。

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

重新規劃項目目錄

引入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相關的代碼管理和初始化流程做的規範些,這些組織方式完全可以應用到生產級別的項目中的

作者:kevinyan

鏈接:https://juejin.im/post/5e4896ef6fb9a07c8334d368

相關文章