上篇文章中我们在使用的开发环境中增加了MySQL
容器,然后介绍了使用database/sql
标准库结合数据库驱动包进行数据库操作的方法。不过它们是相对偏底层的软件包。实际开发经常会使用一些在它的基础上封装的 ORM
库。ORM
的查询使用起来更简单些,语法更富表达力。这篇文章我们主要探究下面这些内容。
gorm
的基本用法ORM
的使用gorm
是一个出色的,对开发人员友好的 Golang
ORM
库,其支持的特性包括:
使用如下命令进行安装:
go get -u github.com/jinzhu/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
放在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 }
经过上面几步的设置后我们就可以在项目里使用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
获取文章中完整的源代码,喜欢我的文章请点赞和收藏。