上篇文章中我们在使用的开发环境中增加了 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 {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"`
}
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).Errorreturn
}
func GetUserById(userId int64) (user *table.User, err error) {user = new(table.User)err = DB().Where("id = ?", userId).First(user).Errorreturn
}
func GetAllUser() (users []*table.User, err error) {err = DB().Find(&users).Errorreturn
}
func UpdateUserNameById(userName string, userId int64) (err error) {user := new(table.User)err = DB().Where("id = ?", userId).First(user).Errorif err != nil {return}user.UserName = userNameerr = DB().Save(user).Errorreturn
}
func DeleteUserById(userId int64) (err error) {user := new(table.User)err = DB().Where("id = ?", userId).First(user).Errorif err != nil {return}err = DB().Delete(user).Errorreturn
}

验证ORM 方法

经过上面几步的设置后我们就可以在项目里使用 gorm访问数据库了,由于我们项目的 main goroutine中运行了 http服务,所以我们使用测试用例对上面 dao包中定义的几个方法进行一下测试。

篇幅原因我就只贴一个 GetAllUsers方法的测试用例了:

func TestGetAllUser(t *testing.T) {tests := []struct {name      stringwantErr   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相关推荐

  1. Go Web 编程--应用 ORM

    上篇文章中我们在使用的开发环境中增加了MySQL容器,然后介绍了使用database/sql标准库结合数据库驱动包进行数据库操作的方法.不过它们是相对偏底层的软件包.实际开发经常会使用一些在它的基础上 ...

  2. easyui datagrid url不请求请求_Go Web编程--深入学习解析HTTP请求

    之前这个系列的文章一直在讲用Go语言怎么编写HTTP服务器来提供服务,如何给服务器配置路由来匹配请求到对应的处理程序,如何添加中间件把一些通用的处理任务从具体的Handler中解耦出来,以及如何更规范 ...

  3. bootstrap网页模板源码_Go Web 编程--超详细的模板库应用指南

    如果你有过Web编程的经验,那么或多或少都听说过或者使用过模板.简而言之,模板是可用于创建动态内容的文本文件.例如,你有一个网站导航栏的模板,其中动态内容的一部分可能是根据当前用户是否登录显示登录还是 ...

  4. Go Web编程--解析JSON请求和生成JSON响应

    现在无论是网站.App.小程序还是移动端H5页面应用,都是采用前端与后端单独部署,相互之间以API接口交互的形式构建而成的.因为在结合可读性.编码数据大小和开发者使用难度上都JSON格式是一个比较好的 ...

  5. Go Web编程--SecureCookie实现客户端Session管理

    在Web应用开发中Session是在用户和服务器之间进行交换的非持久化交互信息.当用户登录时,可以在用户和服务器之间生成Session,然后来回交换数据,并在用户登出时销毁Session.gorill ...

  6. Go Web 编程--如何确保Cookie数据的安全传输

    什么是Cookie Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上.通常, ...

  7. Go Web 编程--超详细的模板库应用指南

    如果你有过Web编程的经验,那么或多或少都听说过或者使用过模板.简而言之,模板是可用于创建动态内容的文本文件.例如,你有一个网站导航栏的模板,其中动态内容的一部分可能是根据当前用户是否登录显示登录还是 ...

  8. Go Web编程--深入学习解析HTTP请求

    之前这个系列的文章一直在讲用 Go语言怎么编写HTTP服务器来提供服务,如何给服务器配置路由来匹配请求到对应的处理程序,如何添加中间件把一些通用的处理任务从具体的Handler中解耦出来,以及如何更规 ...

  9. Go Web编程--应用数据库

    今天我们继续接着前几篇关于 GoWeb编程的文章往下延伸.在 Web应用程序中几乎每个应用场景都需要存储和检索数据库中的数据.当你处理动态内容,为用户提供表单以输入数据或存储登录名和密码凭据以供用户进 ...

最新文章

  1. Openpose——windows编译(炒鸡简单)
  2. React中的唯一标识key(用index VS id)和key的选择
  3. 双列集合Map的实现类
  4. iptables控制较复杂案例
  5. BIO、NIO、AIO的简单个人理解,同步异步和阻塞非阻塞的简单理解
  6. APUE C内存空间详解图
  7. 金蝶盘点机PDA轻松扫码生产领料,生产型企业进销存条码管理软件
  8. 个人博客的Travis持续集成之路
  9. [经验教程]在拼多多上发起拼单和参与拼单有什么区别?
  10. java游戏开局选宠物可以转职,创世之光人物资料及转职大全
  11. Commerzbank和Sparkasse开始支持Apple Pay
  12. oracle建表案例,oracle创建表语句
  13. 微信墙php_微信上墙PHP源码
  14. 主成分分析(PCA)方法步骤以及代码详解
  15. 八种可以简单判断否属于过敏体质的表现,符合四条就是了
  16. 带你玩转超级列表框(1-4)雪山灵狐
  17. 网站UI设计10大原则
  18. 1.腾讯轻服务器K3S环境配置
  19. ffmpeg+nvidia解码SDK+GPU实现视频流硬解码成Mat
  20. Rust开发crates.io换国内镜像源

热门文章

  1. jsp脚本、jsp标准动作、EL表达式、JSTL标签
  2. thinkphp5 memcached 安装、调用、链接
  3. Radware LP 增加线路接口操作
  4. 【Gitlab+Jenkins+Ansible】构建自动化部署
  5. SQL Server 2008 R2 数据库安装
  6. 桌面桌面虚拟化-Vmware horizon 7相关文件共享
  7. 关于中层管理者的会议态度
  8. 如何获取Agile PLM Business Object 对应Agile对象的属性?
  9. MPLS/×××江湖恩仇录笔记
  10. Dockerfile中CMD和ENTRYPOINT的区别