阅读 401

【Go开源宝藏】GORM 专场 (含思维导图) | 持续更新

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金

写在前面

本人只是一个Go语言的初学者,这篇文只是把我平常经常用到的都总结起来而已。 具体详细的内容建议到去GORM的中文文档查看。 当然这篇文章也会持续更新,记录我的CURD打磨过程 这篇文章也会持续更新哒

思维导图

请添加图片描述

@TOC

在这里插入图片描述

1. 简介

  • 介绍:一个Go语言开发的ORM库,是字节的一位大牛写哒!!国产框架!
  • 下载:go get -u github.com/jinzhu/gorm

流程:

  1. 导入Gorm
  2. 导入数据库驱动
  3. 定义数据库模型
  4. 与数据库连接
  5. 字段迁移映射
  6. 增、删、改、查
  7. 关闭数据库连接

2. 数据库驱动

数据库驱动导入
mysqlgithub.com/go-sql-driver/mysqlgithub.com/jinzhu/gorm/dialects/mysql
postgresqlgithub.com/lib/pqgithub.com/jinzhu/gorm/dialects/postgres
sqlitegithub.com/mattn/go-sqlite3github.com/jinzhu/gorm/dialects/sqlite
sqlservergithub.com/jinzhu/gorm/dialects/mssql

3. 连接(MySQL为例子)

配置ini文件

[mysql]
Db = mysql
DbHost = 127.0.0.1
DbPort = 3306
DbUser = root
DbPassWord = root
DbName = carsys
复制代码

读取配置文件

	file, err := ini.Load("./conf/config.ini")
	if err != nil {
		fmt.Println("配置文件读取错误,请检查文件路径:", err)
	}
	LoadMysqlData(file)
复制代码

数据库路径拼接

path := strings.Join([]string{DbUser, ":", DbPassWord, "@tcp(", DbHost, ":", DbPort, ")/", DbName, "?charset=utf8&parseTime=true"}, "")  // 路径拼接
model.Database(path)  	// 连接数据库
复制代码

连接数据库,这个是放在了model包,所以后面的操作都是model.DB

var DB *gorm.DB  //全局的数据库入口

func Database(connString string) {
	db, err := gorm.Open("mysql", connString)
	db.LogMode(true)
	if err != nil {
		panic(err)
	}
	if gin.Mode() == "release" {
		db.LogMode(false)
	}
	db.SingularTable(true)         //默认不加复数s,不然他的每个数据后面都会加上和s比如user变成users
	db.DB().SetMaxIdleConns(20)  	//设置连接池,空闲
	db.DB().SetMaxOpenConns(100) 	//设置打开最大连接
	db.DB().SetConnMaxLifetime(time.Second * 30)
	DB = db
	migration() //模型迁移映射到数据库
}
复制代码

4. 迁移(MySQL为例子)

自动迁移数据库到最新版本,最会添加列和索引,不会修改类型删除列

4.1 构建数据库表

这个gorm.Model是自动加上create_timeupdate_timedelete_time

查看源码里面得gorm中得model在这里插入图片描述

然后我们可以像这样直接去定义一个struct模型,准备映射到数据库中,就不用写sql语句了。 后面可以加上tag就像是gorm:"unique"这样的,表示这个是唯一的。

import "github.com/jinzhu/gorm"

type User struct {
	gorm.Model
	UserName       string `gorm:"unique"` 
	Email          string 
	PasswordDigest string
	Nickname       string `gorm:"unique"`
	Status         string
	Limit          int   // 0 非管理员  1 管理员
	Type           int    // 0表示用户  1表示商家
	Avatar         string `gorm:"size:1000"`
	Monery 		    int
}
复制代码

4.2 数据库模型迁移

gorm提供了一个AutoMigrate的方法,很方便地进行映射迁移的工作。

func migration() {
	//自动迁移模式
	DB.Set("gorm:table_options", "charset=utf8mb4").
		AutoMigrate(&User{})
}
复制代码

4.3 tags束缚

4.3.1 一对多

语义场景:评论和帖子的关系。一个帖子有很多的评论,但是这个评论只能在这个帖子下面。

所以可以通过foreignkey进行外键的束缚。外键到Social这个模型。关系标识为social_id

type Comment struct {
	gorm.Model
	Content    string
	ParentId   uint   	// 父评论ID
	UserID     uint   	// 用户ID
	ReplyName  string 	// 回复者名字
	UserName   string
	UserAvatar string
	SocialId   uint  `gorm:"foreignkey:Social;association_jointable_foreignkey:social_id"` //社区帖子ID
	Children   []Comment `gorm:"foreignkey:ParentId"`
}
复制代码

4.3.2 多对多

语义环境:一个用户可以有多个好友,反过来也可以。所以就是user表自己的多对多。

使用tags是many2many

type User struct {
	gorm.Model
	Relations 	   []User `gorm:"many2many:relation; association_jointable_foreignkey:relation_id"`
	UserName       string
	Email          string 	//`gorm:"unique"`
	Avatar         string 	`gorm:"size:1000"`
	Phone 		   string
	BanTime 	   int
	OpenID 		   string 	`gorm:"unique"`
}
复制代码

这样的多对多的关联,就会多创建一个表来存两者的关系。


5. 增

5.1 一对一

create函数进行创建。

category := model.Category{
		CategoryName: service.CategoryName,
		EnglishName:service.EnglishName,
}
err := model.DB.Create(&category).Error
复制代码

5.2 一对多

语义环境:这里就是一评论表,socialId就是帖子, 一个帖子对应多个评论,和一对一是 一样的创建方式

	var comment model.Comment
	comment = model.Comment{
		UserID:    user.ID,
		Content:   service.Content,
		ParentId:  service.ParentId,
		UserName:  user.UserName,
		UserAvatar:  user.Avatar,
		SocialId: service.SocialID,
	}
	err := model.DB.Create(&comment).Error
复制代码

5.3 多对多

语义环境:用户表,用户和用户之间建立多对多联系

type User struct {
	gorm.Model
	Relations 	   []User `gorm:"many2many:relation; association_jointable_foreignkey:relation_id"`
	UserName       string
	Email          string 	//`gorm:"unique"`
	Avatar         string 	`gorm:"size:1000"`
	Phone 		   string
	BanTime 	   int
	OpenID 		   string 	`gorm:"unique"`
}
复制代码

选出user表中的两个元素。然后进行关联绑定。

	var user model.User
	var friend model.User
	model.DB.Model(&friend).Where(`id = ? `,id).First(&friend)  			//被关注者
	model.DB.Model(&user).Where(`id = ?`,userId).First(&user)			//关注者
	err := model.DB.Model(&user).Association(`Relations`).Append([]model.User{friend}).Error
复制代码

这条语句就是把这个user和这个friend建立联系,存放在Relations表里面。


6. 删

6.1 一对一

GORM中的delete删除是软删除,就是虽然是删除了,但是并没有完全删除,而是保留在数据中但是查询并不查询这条已经删除的语句。

  • 单记录删除

先找到这条数据

	var social model.Society
	code := e.Success
	err := model.DB.First(&social, id).Error // 根据id找到这个social,记得传地址。
复制代码

然后进行删除即可

	err = model.DB.Delete(&social).Error
复制代码
  • 批量删除
 db.Where("user_name like ? ","%Fan%").Delete(User{})
复制代码
  • 永久删除:在数据库直接清空了,不是软删除了
db.Unscoped().Where("name Like ?","%6%").Delete(User{})
复制代码

6.2 一对多

其实一对一就是特殊的一对多

删除和一对一其实是一样的操作。

	var social model.Society
	model.DB.First(&social, id)  // 找到这个帖子
	model.DB.Delete(&social)   // 将其进行删除
复制代码

6.3 多对多

找到这两者的关系,然后进行关联删除即可。就会从Relations表中把这两者的关系进行删除。

	var user model.User
	var friend []model.User
	model.DB.Model(&friend).Where(`id = ?`,id).First(&friend)  			//被关注者
	model.DB.Model(&user).Where(`id = ?`,userId).First(&user)	//关注者
	err := model.DB.Model(&user).Association(`Relations`).Delete(friend).Error
复制代码

7. 改

7.1 一对一

  • Save

会保存所有的字段,即使字段是零值

social := model.Society{
		CategoryID:    service.CategoryID,
		Title:         service.Title,
		Content:       service.Content,
		Picture:       service.Picture,
		CategoryName:  service.CategoryName,
	}
err := model.DB.Save(&social).Error
复制代码
  • Update

当使用Update更新单个列时,需要指定条件,否则会返回ErrMissingWhereClause错误。

model.DB.Model(model.User{}).Where("open_id=?", openid).Update("phone", phone)
复制代码
  • Updates

批量更新,只会更新指定字段,但是如果是空值的情况下是不更新的成空值的。

只更新指定字段

model.DB.Table("user").Where("user_name = ?","FanOne").
Updates(map[string]interface{}{"phone":"10001","email":"fanone@qq.com"})
复制代码

只更新更改的非零值的字段

model.DB.Model(&User{}).Where("user_name = ?","FanOne").
Updates(User{Phone:"10001",Email:"fanone@666.com"})
复制代码

7.2 一对多

直接更新字段,同上。

7.3 多对多

由于多对多的是根据关系来关联的,所以更新字段关系是无关的,同上。


8. 查

  • First 是找到第一个
  • Last 是找到最后一个
  • Find 是找到全部

8.1 一对一

语义环境:用户表

type User struct {
	gorm.Model
	Relations 	   []User `gorm:"many2many:relation; association_jointable_foreignkey:relation_id"`
	UserName       string
	Email          string 	//`gorm:"unique"`
	Avatar         string 	`gorm:"size:1000"`
	Phone 		      string
	BanTime 	      int
	OpenID 		   string 	`gorm:"unique"`
}
复制代码

8.1.1 基本查询

  • 查询第一个
var u1 User
db.First(&u1)
fmt.Println(u1)
复制代码
  • 查询最后一个
var u2 User
db.Last(&u2)
fmt.Println(u2)
复制代码
  • 按主键获取
var u3 User
db.First(&u3,2) //不指定的话,默认主键是id
fmt.Println(u3)
复制代码
  • 按条件查询一条
var u4 User
db.First(&u4,"user_name=?","FanOne") //不指定的话,默认主键是id
fmt.Println(u4)
复制代码
  • 获取所有数据
var u5 []User
db.Find(&u5) //查询所有的数据信息
fmt.Println(u5)
复制代码

8.1.2 Where条件

  • 单一条件
var u1 User
db.Where("user_name = ?","FanOne").First(&u1)
复制代码
  • 嵌套查询
var u1s []User
db.Where("user_name=?","fanone").Where("phone=?","10086").Find(&u1s)
复制代码
  • 多条件
var u2 User
db.Where("user_name = ? AND phone = ?","FanOne","10086").First(&u2)
复制代码
  • 模糊查询 记得加上%
var u3 User
db.Where("user_name LIKE ?","%Fan%").First(&u3)  //返回第一个user_name与Fan相近的数据
var u3s []User
db.Where("user_name LIKE ?","%Fan%").Find(&u3)   //返回多个user_name与Fan相近的数据
复制代码
  • 范围查询
var u4 []User
db.Where("id > ?","FanOne","10").Find(&u2) // 找到id>10那部分的数据
复制代码
  • 数组形式
var u5 []User
db.Where("user_name in (?)",[]string{"Fan","One"}).Find(&u5)
复制代码
  • 结构体形式
var u6 User
db.Where(&User{UserName:"FanOne"}).First(&u6)
复制代码
  • map形式
var u7 User
db.Where(map[string]interface{}{"user_name":"fan","phone":"10086"}).First(&u7)
复制代码

8.1.3 Not条件

  • 查询UserName != FanOne的数据,返回列表
var u1 []User
db.Not(&User{UserName:"FanOne"}).Find(&u1)
复制代码
  • 查询UserName != FanOne的数据,返回第一条数据
var u2 User
db.Not("user_name","FanOne").First(&u2)
复制代码
  • 查询UserName != fan的数据,返回第一条数据
var u3 User
db.Not("user_name = ?","fan").First(&u2)
复制代码
  • 查询 UserName 不在这个字符串列表当中的数据,返回第一条数据
var u4 User
db.Not("user_name",[]string{"fan","one"}).First(&u4)
复制代码
  • 查询 UserName 不在这个字符串列表当中的数据,返回列表
var u5 []User
db.Not("user_name in (?)",[]string{"fan","one"}).Find(&u5)
复制代码

8.1.4 选择查询

  • 只查询user_name这一列的数据
var u1 []User
db.Select("user_name").Find(&u1)
复制代码
  • 只查询user_name和phone这两列的数据,字符串数组形式
var u1 []User
db.Select([]string{"user_name","phone"}).Find(&u1)
复制代码
  • 只查询user_name和phone这两列的数据,字符串数组形式
var u1 []User
db.Select("user_name , phone").Find(&u1)
复制代码

8.1.5 排序

默认排序是升序的

var u1s,u2s []User
db.Order("name desc").Find(&u1s)  //按照名字降序
db.Order("name asc",true).Find(&u1s)  //按照名字升序
复制代码

8.1.6 分页

Limit 页面大小 Offset 页数

var us []User
db.Order("id asc").Offset(5).Limit(3).Find(&us)
复制代码

8.1.7 数量

var count int
db.Model(&User{}).Count(&count)
复制代码
var count int
db.Model(&User{}).Where("user_name Like ?","%6%").Count(&count)
复制代码
var count int
db.Table("users").Where("user_name Like ?","%6%").Count(&count)
复制代码

8.1.8 原生SQL

当我们要进行比较复杂的查询的时候,可以通过这个原生sql进行查询。进行并查询操作

	query := "SELECT * FROM `work_time` WHERE `work_time`.`deleted_at` IS NULL AND month = "+ `"`+ data.Month +`"`

	if data.Search!="" {
		query += ` AND (( mask LIKE "`+"%"+data.Search +"%"+ `" ) OR ( number LIKE "`+"%"+data.Search+"%"+ `" ))`
	}
	if data.DepartmentName!="" {
		query += " AND department_name LIKE " + `"`+"%" +data.DepartmentName+"%" + `"`
	}
	if data.WorkType!="" {
		query += " AND work_type LIKE " + `"`+"%" +data.WorkType+"%" + `"`
	}
	if data.EmployeeStatus!="" {
		query += " AND employee_status LIKE " + `"`+"%" +data.EmployeeStatus+"%" + `"`
	}
	if data.EmployeeType!="" {
		query += " AND employee_type LIKE " + `"`+"%" +data.EmployeeType+"%" + `"`
	}
	if data.Status!="" {
		query += " AND status LIKE " + `"`+"%" +data.Status+"%" + `"`
	}
	if err := db.Offset((data.PageNum - 1) * data.PageSize).Limit(data.PageSize).Raw(query).Scan(&workTimeList).Error; err != nil {
		code = e.LEVEL_ERROR
		return serializer.Response{
			Level: code,
			Data:  e.GetMsg(code),
			Error: err.Error(),
		}
	}
复制代码

8.2 一对多

语义场景 Comment是评论表,Social是帖子表 一个帖子对应多个评论,所以在多端进行关联

type Comment struct {
	gorm.Model
	Content    string
	ParentId   uint   	// 父评论ID
	UserID     uint   	// 用户ID
	ReplyName  string 	// 回复者名字
	UserName   string
	UserAvatar string
	SocialId   uint  `gorm:"ForeignKey:Social;AssociationForeignKey:social_id"` //社区帖子ID
	Children   []Comment `gorm:"ForeignKey:Comment;AssociationForeignKey:comment_id"`
}
复制代码
  • 查单个用Related关联
	var comment model.Comment 
	model.DB.Model(&comment).
	Related(&comment.SocialId , "social_id").Find(&comment)
复制代码
  • 查多个用Preload预加载
	var comment []model.Comment
	model.DB.Model(&comment).Preload("Children").Find(&comment)
复制代码

8.3 多对多

语义场景 用户自己对自己多对多。类似好友。一个人可以有多个好友,一个好友又可以包括其他好友。

type User struct {
	gorm.Model
	Relations 	    []User `gorm:"many2many:relation; association_jointable_foreignkey:relation_id"`
	UserName       string
	Email          string 	//`gorm:"unique"`
	Avatar         string 	`gorm:"size:1000"`
	Phone 		   string
	BanTime 	   int
	OpenID 		   string 	`gorm:"unique"`
}
复制代码

user表中的多对多查询,可以通过这个Association进行联合查询查询与之相关的数据信息。

	var user model.User
	var friendsList []model.User
	model.DB.Table("user").Where("id = ?",id).First(&user)
	err := model.DB.Model(&user).Association("Relations").
	Limit(service.PageSize).Offset((service.PageNum-1)*service.PageSize).
	Find(&friendsList).Error
复制代码

9. 错误总结

错误 1:Error 1103: Incorrect table name

不知道是哪个表

	var employees []model.Employee
	var count int
	db := model.DB
复制代码

改成

	var employees []model.Employee
	var count int
	db := model.DB.Model(model.Employee{})
复制代码

错误2:sql: no rows in result set

注意: Count()查询必须在where条件之后,limit,offset分页之前!

如果写在limit,offset 分页 之前之后,在第二页开始就会报错 sql: no rows in result set

count也不能太前,否则查询出来的总数将是所有数据总数,非条件过滤后的条数

	db.Limit(data.PageSize).Offset((data.PageNum-1)*data.PageSize).Find(&employees).Count(&count)
复制代码

改成

	db.Count(&count).Limit(data.PageSize).Offset((data.PageNum-1)*data.PageSize).Find(&employees)
复制代码
文章分类
后端
文章标签