Loading

gin-gorm操作数据库

GORM 是 Golang 的一个 orm 框架。简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM框架可以让我们更方便的操作数据库。

GORM 官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server

安装

gorm.io/gorm  // gorm
gorm.io/driver/mysql // mysql驱动

配置连接数据库

package models

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// gorm.DB 是GORM的数据库操作对象
// DB对象供整个项目使用,所以应该是一个全局的对象
var DB *gorm.DB

var err error

func init() {

	/*
		    数据库连接参数:
		        "数据库账号:数据库密码@tcp(地址:端口)/数据库名?charset=utf8mb4&parseTime=True&loc=Local"

			parseTime=True:
				parseTime 是一个连接参数,用于告诉 GORM 在将数据库时间戳类型的值解析为 Go 时间类型时是否启用自动解析。
				当 parseTime=True 时,GORM 会自动将数据库中的时间戳类型的值解析为 Go 的 time.Time 类型。
				这使得你可以直接将数据库中的时间戳字段映射到 Go 的时间类型,而无需手动进行类型转换

			loc=Local:
				loc 是一个连接参数,用于指定数据库连接的时区位置。
				当 loc=Local 时,GORM 会将数据库的时间戳类型的值作为本地时间进行解析和处理。
				这意味着数据库中存储的时间戳值将根据本地时区进行转换,而不是使用默认的 UTC 时区

	*/

	dsn := "root:123456@tcp(127.0.0.1:3306)/gindb?charset=utf8mb4&parseTime=True&loc=Local"

	//gorm.Open用于打开与数据库的连接,使用 gorm.Open 函数来建立与数据库的连接。该函数接受两个参数:数据库驱动、配置选项
	// mysql.Open(dsn) 创建 MySQL 数据库驱动程序实例的函数调用
	// &gorm.Config{} 是一个 GORM 库中用于配置的结构体实例,用于传递给 gorm.Open 函数以自定义数据库连接和操作的行为
	DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})

}

定义数据库模型

1、结构体的名称必须首字母大写 ,并和数据库表名称对应。例如:表名称为 user 结构体名称定义成 User,表名称为 article_cate 结构体名称定义成 ArticleCate

2、结构体中的字段名称首字母必须大写,并和数据库表中的字段一一对应。例如:结构体中的 Id 和数据库中的 id 对应,Username 和数据库中的 username 对应,AddTime 和数据库中的 add_time 字段对应

3、默认情况表名是结构体名称的复数形式。如果我们的结构体名称定义成 User,表示这个模型默认操作的是 users 表

4、使用结构体中的自定义方法 TableName 改变结构体的默认表名称

package models

// GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间

type User struct { // 对应复数users表
	// 定义users表对应的字段
	Id       int
	Username string
	Age      int
	Email    string
	AddTime  int // 时间戳
}

// 配置操作数据库的表名称,如果不配置默认是结构体名称的复数形式,也就是users
func (User) TableName() string {
	return "user"
}

数据库CURD

数据查询
查询全部数据
func (a ApiController) ApiGetInfo(context *gin.Context) {
	// 查询数据库,因为Users表有很多数据,定义成一个切片,存储每一个查询到的结构体
	userList := []models.User{}
	// 拿到声明好的全局的DB对象,该模块定义init方法,调用的时候自动执行连接数据库
	// 通过Find方法,将查询到的数据传给userList,参数为指向模型结构体的指针或指向切片的指针
	models.DB.Find(&userList)
	context.JSON(http.StatusOK, userList)

}
查询一条数据
	// 查询ID为3的数据
	userList := models.User{Id: 3}
	models.DB.Find(&userList)
条件查询
	userList := []models.User{}
	// 查询age大于20的用户
	models.DB.Where("age>20").Find(&userList)
	userList := []models.User{}
  // ?是占位符,等同于age>3
	models.DB.Where("age>?",3).Find(&userList)
	userList := []models.User{}
	// 查询ID大于3并且小于9的数据
	models.DB.Where("id>3 AND id < 9").Find(&userList)
	userList := []models.User{}
	// 查询ID在3、5、6中的数据
	models.DB.Where("id in (?)",[]int{3,5,6}).Find(&userList)
	userList := []models.User{}
	// 模糊查询,查询username包含li的数据
	models.DB.Where("username like ?", "%li%").Find(&userList)
	userList := []models.User{}
	// 查询 ID在3和9之间的数据
	models.DB.Where("id between ? and ?", 3, 9).Find(&userList)
	userList := []models.User{}
	// 查询 id=3 or id=6 or id =8的数据
	models.DB.Where("id = ? or id =  ? or id = ?", 3, 6,8).Find(&userList)

  // or语句的第二种写法
	models.DB.Where("id = ?",3).Or("id=?",5).Or("id=?",7).Find(&userList)

	userList := []models.User{}
	
	// 查询指定字段,非指定查询字段默认为零值或者空,如果只想要对应两个字段,定义一个新的结构体
	models.DB.Select("id,title").Find(&userList)
	userList := []models.User{}
	// asc代表升序,desc代表降序
	// 查询所有数据 使用id降序排序、使用add_time升序排序
	models.DB.Order("id desc").Order("add_time asc").Find(&userList)
	userList := []models.User{}
	// 使用limit查询两条数据
	models.DB.Order("id desc").Limit(2).Find(&userList)
	userList := []models.User{}
	// Offset进行分页,跳过前两条数据,从第三条开始查询,
	models.DB.Offset(2).Limit(2).Find(&userList)
	userList := []models.User{}
	
	// 统计查询数量
	var count int64
	models.DB.Find(&userList).Count(&count)
创建数据
func (a ApiController) ApiSetInfo(context *gin.Context) {
	user := models.User{
		Username: "lll",
		Age:      11,
		Email:    "111",
		AddTime:  10101010,
	}
	// 使用create创建数据
	models.DB.Create(&user)
	context.String(http.StatusOK, "ok")
}
修改数据
先查询再更新(常用)
func (a ApiController) ApiSetInfo(context *gin.Context) {
	// 结构体指定字段可以查询 ID=2的数据,如果不指定更新哪个数据,则会创建一个新的数据
	user := models.User{Id: 2}
	// 将username修改为setname
	user.Username = "setname"
	// 邮箱修改为 123.qq.com
	user.Email = "123.qq.com"

	// 将ID为2的数据的username和email更新
	models.DB.Save(&user)
	context.String(http.StatusOK, "ok")
}

func (a ApiController) ApiSetInfo(context *gin.Context) {
	user := models.User{}
	// 查找ID=6的数据
	models.DB.Where("id = ?", 3).Find(&user)
	// 修改数据
	user.Username = "12333"
	user.Email = "444"
	// 保存修改数据
	models.DB.Save(&user)
	context.String(http.StatusOK, "ok")

}
更新单个字段
	user := models.User{}
	// 将user表 ID=3的username字段的值改为1233
	models.DB.Model(&user).Where("id= ?", 3).Update("username", "1233")
更新多个字段
func (a ApiController) ApiSetInfo(context *gin.Context) {
	user := models.User{}
  // 传入更新的结构体对象 更新ID为的数据的其他字段
	models.DB.Model(&user).Updates(models.User{
		Id:       1,
		Username: "11",
		Age:      1,
		Email:    "1",
		AddTime:  1,
	})
	context.String(http.StatusOK, "ok")

}
删除数据
	// 删除ID=4的数据
	user := models.User{Id: 4}
	models.DB.Delete(&user)
	// 删除username=li的数据
	user := models.User{}
	models.DB.Where("username = ?", "li").Delete(&user)
使用原生sql
	userList := []models.User{}

	// 使用sql语句进行查询,赋值给userlist
	models.DB.Raw("select id,username from user where username = ?", "li").Scan(&userList)
	// 删除id为3的数据
	models.DB.Exec("delete from user where id = ?", 3)
	// 修改数据id=3的username为li
	models.DB.Exec("update user set username = `li` where id = ?", 3)
	var count int64
	// 统计查询数量
	models.DB.Raw("select count(1) from user ").Scan(&count)

gorm表关联查询

一对一
模型定义

User表对应用户,City对应城市,一个用户只能在一座城市

// 结构体模型
type User struct {
	Id       int
	Username string
	Age      int
	Email    string
	AddTime  int
	CityId   int  // 外键
	City     City // 对应的City模型
}

func (User) TableName() string {
	return "user"
}

// 外键表
type City struct {
	Id   int
	Name string
}

func (City) TableName() string {
	return "city"
}
// 自定义外键字段
type User struct {
	Id       int
	Username string
	Age      int
	Email    string
	AddTime  int
	CId      int  // 外键默认是模型_id,我们可以自己定义外键字段
  City     City `gorm:"foreignKey:CId;references:id"` // 指定外键为c_id,对应的主键是id
  // 外键默认对应是表名_id,如果是表名_id,可以不配置
  // 主键默认对应是id,如果就是id,可以不配置
  
}
数据查询
	userList := []models.User{}
	// 查询数据,并且显示对应的City模型的分类
  // 传参是结构体定义的另一个结构体模型的变量
	models.DB.Preload("City").Find(&userList)
	context.JSON(http.StatusOK, userList)
一对多
模型定义

User表对应用户,City对应城市,一座城市可以有多个用户

package models

type User struct {
	Id       int
	Username string
	Age      int
	Email    string
	AddTime  int
	CityId   int
	City     City
}

func (User) TableName() string {
	return "user"
}

type City struct {
	Id   int
	Name string
   // 外键模型
	User []User `gorm:"foreignKey:city_id"` // 指定外键为city_id
}

func (City) TableName() string {
	return "city"
}

数据查询
	valueList := []models.City{}
	// 获取所有的City分类对应的数据
	models.DB.Preload("User").Find(&valueList)
多对多

User表对应用户,City对应城市,一个用户可以去多个城市,一个城市可以有多个用户

模型定义
package models

package models

// User表
type User struct {
	Id       int
	Username string
	Age      int
	Email    string
	AddTime  int
	CityId   int
	// 多对多关联关系的中间表为user_city
	City []City `gorm:"many2many:user_city;"`
}

func (User) TableName() string {
	return "user"
}

// city表
type City struct {
	Id   int
	Name string
  	// 多对多关联关系的中间表为user_city
	User []User `gorm:"many2many:user_city;"`
}

func (City) TableName() string {
	return "city"
}

// 关联关系表
type UserCity struct {
	UserID int
	CityID int
}

func (UserCity) TableName() string {
	return "user_city"

}

数据查询
	// 查询user表,并关联查询user对应的city
	userList := []models.User{}
	models.DB.Preload("City").Find(&userList)
	// 查询uusername=li的数据和关联city
	userList := []models.User{}
	models.DB.Preload("City").Where("username = ?", "li").Find(&userList)
	// 查询City表的关联数据
	cityList := []models.City{}
	models.DB.Preload("User").Find(&cityList)
	// limit条件查询
	cityList := []models.City{}
	models.DB.Preload("User").Limit(1).Find(&cityList)

gorm使用事物

事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全执行,要么全不执行

禁用默认事物

事物默认是开启的,为了确保数据一致性,GORM 会在事务里执行写入操作(创建、更新、删除)。如果没有这方面的要求,可以在初始化时禁用它,这将获得大约 30%+ 性能提升

// 连接数据库的时候配置 SkipDefaultTransaction为true 表示禁用事物	
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{SkipDefaultTransaction: true})
使用事物
// 如果连接数据库的禁用事物,则不能使用事物
// 开启事物
	tx := models.DB.Begin()

	// 抛出异常
	defer func() {
		if r := recover(); r != nil {
			// 遇到异常的时候回滚
			tx.Rollback()
			return
		}
	}()

	// 事物操作,应该用tx代替db
	// 更新id为1的数据
	user := models.User{Id: 1}
	tx.Find(&user)
	user.Username = "li"
	// 更新username数据,如果更新的时候遇到err,进行回滚操作
	if err := tx.Save(&user).Error; err != nil {
		tx.Rollback()
		return
	}

	// 可以执行多个不同的操作,最后统一提交
	// 如果没有错误,则执行提交的操作
	tx.Commit()
posted @ 2024-03-05 11:09  木子七  阅读(130)  评论(0编辑  收藏  举报