GORM使用

1 基本使用

请添加图片描述

请添加图片描述

1.1 连接数据库

func BasicUsage() {
	//定义DSN
	dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	//连接服务器(池)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}
	//open成功
	fmt.Println(db)
}

1.2 基于模型迁移表结构

请添加图片描述

  • 自动根据结构体在对应数据库生成表结构
// 创建模型
type Article struct {
	//嵌入基础模型
	gorm.Model
	//定义字段
	Subject     string
	Likes       uint
	Published   bool
	PublishTime time.Time
	AuthorID    uint
}

func BasicUsage() {
	//定义DSN
	dsn := "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	//连接服务器(池)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}
	//open成功
	//fmt.Println(db)
	//基于模型完成表结构(设计)的迁移(定于)
	if err := db.AutoMigrate(&Article{}); err != nil {
		log.Fatal(err)
	}
}

1.3 基本的crud

var DB *gorm.DB
func init() { //初始化操作
	//定义DSN
	const dsn = "root:123456@tcp(127.0.0.1:3306)/gorm?charset=utf8mb4&parseTime=True&loc=Local"
	//连接服务器(池)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		log.Fatal(err)
	}
	//open成功
	//fmt.Println(db)
	//基于模型完成表结构(设计)的迁移(定于)
	if err := db.AutoMigrate(&Article{}); err != nil {
		log.Fatal(err)
	}
	DB = db
}

// 增
func Create() {
	//构建Article类型数据
	article := &Article{
		Subject:     "gorm 增操作",
		Likes:       0,
		Published:   true,
		PublishTime: time.Time{},
		AuthorID:    2,
	}
	//DB.Create 完成数据库的insert
	if err := DB.Create(article).Error; err != nil {
		log.Fatal(err)
	}
	//print
	fmt.Println(article)
}

// 查
// Find()多个 First()单个
func Retrieve(id uint) {
	//初始化Article模型,零值
	article := &Article{}
	//DB.First()
	if err := DB.First(article, id).Error; err != nil {
		log.Fatal(err)
	}
	//print
	fmt.Println(article)
}

//更新
/*
1.先确定更新的对象
2.设置对象属性字段
3.将对象存储
*/
func Update() {
	//获取需要更新的对象
	article := &Article{}
	if err := DB.First(article, 1).Error; err != nil {
		log.Fatal(err)
	}
	//更新对象字段
	article.AuthorID = 23
	article.Likes = 512
	article.Subject = "新的文章标题"
	//存储 DB.save()
	if err := DB.Save(article).Error; err != nil {
		log.Fatal(err)
	}
}

// 删除
func Delete() {
	//获取模型对象
	article := &Article{}
	if err := DB.First(article, 2).Error; err != nil {
		log.Fatal(err)
	}
	//DB.delete()
	if err := DB.Delete(article).Error; err != nil {
		log.Fatal(err)
	}
}

1.4 debug 日志

请添加图片描述

// debug
func Debug() {
	//insert
	article := &Article{
		Subject:  "这是一条新插入的数据",
		AuthorID: 50,
	}
	if err := DB.Create(article).Error; err != nil {
		log.Fatal(err)
	}
	//select
	if err := DB.Debug().First(article, article.ID).Error; err != nil {
		log.Fatal(err)
	}
}

请添加图片描述
请添加图片描述

// 自定义日志
var logWriter io.Writer
logWriter, _ = os.OpenFile("./sql.log", os.O_CREATE|os.O_APPEND, 0644) //因为全局都要用 所以不写 	logWriter.Close()
customLogger := logger.New(log.New(logWriter, "\n", log.LstdFlags),
	logger.Config{
		SlowThreshold:             200 * time.Millisecond, //慢查询阈值
		LogLevel:                  logger.Info,
		IgnoreRecordNotFoundError: false, //保持记录不存在的错误
		//不彩色化
		Colorful: false,
	})
//连接服务器(池)
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
	//设置为自定义日志
	Logger: customLogger,
})

在这里插入图片描述

2 模型定义

2.1 表名定义

请添加图片描述
请添加图片描述

请添加图片描述

	//连接服务器(池)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		//设置为自定义日志
		Logger: customLogger,
		//设置默认的命名策略的选项
		NamingStrategy: schema.NamingStrategy{
			TablePrefix:   "msb_",
			SingularTable: true,
			NameReplacer:  nil,
			NoLowerCase:   false,
		},
	})
	if err != nil {
		log.Fatal(err)
	}

请添加图片描述

  • 生成表名为 my_box

请添加图片描述

func Migrate() {
	if err := DB.AutoMigrate(&Post{}, &Category{}, &PostCategory{}, &Box{}); err != nil {
		log.Fatal(err)
	}
}

在这里插入图片描述

2.2 字段类型映射

请添加图片描述

2.3 指针类型与非指针类型区别

请添加图片描述
请添加图片描述

  • 若表中字段不为null,则使用T类型就够了 因为不存在例如(数据库中为null,对应到结构体中值为0,则0到底是不是有用的值)

2.4 自定义字段类型

请添加图片描述

type CustomTypeModel struct {
	gorm.Model
	FTime     time.Time
	FNullTime sql.NullTime

	Fstring     string
	FNullString sql.NullString

	FUUID     uuid.UUID
	FNullUUID uuid.NullUUID
}

func CustomType() {
	//初始化模型
	ctm := &CustomTypeModel{}
	//迁移数据表
	DB.AutoMigrate(ctm)
	//创建
	ctm.FTime = time.Time{}            //零值
	ctm.FNullTime = sql.NullTime{}     //零值
	ctm.Fstring = ""                   //零值
	ctm.FNullString = sql.NullString{} //零值
	ctm.FUUID = uuid.New()
	ctm.FNullUUID = uuid.NullUUID{}
	DB.Create(ctm)
	//查询
	DB.First(ctm, ctm.ID)
	//判断字段是否为NULL
	if ctm.Fstring == "" {
		fmt.Println("FString is NULL")
	} else {
		fmt.Println("FString is NOT NULL")
	}
	if ctm.FNullString.Valid == false {
		fmt.Println("FNullString is NULL")
	} else {
		fmt.Println("FNullString is NOT NULL")
	}
}

2.5 字段标签设置字段属性

请添加图片描述

type FieldTag struct {
	gorm.Model
	//string类型处理
	FStringDefault string   `gorm:`
	FTypeChar      string   `gorm:"type:char(32)"`
	FTypeVarchar   string   `gorm:"type:varchar(255)"`
	FTypeText      string   `gorm:"type:text"`
	FTypeBlob      []string `gorm:"type:blob"`
	FTypeEnum      string   `gorm:"type:enum('GO','GORM','MySQL')"` //枚举
	FTypeSet       string   `gorm:"type:set('GO','GORM','MySQL')"`  //集合
	FColNum        string   `gorm:"column:cus tom_column_name"`
	//默认是NULL
	FColNotNull string `gorm:"type:varchar(255);not null"`
	FColDefault string `gorm:"type:varchar(255);default: gorm middle ware;"`
	FColComment string `gorm:"type:varchar(255);comment:带有注释的字段"`
}

2.6 索引和约束管理

请添加图片描述

请添加图片描述

type IAndC struct {
	//基础索引类型
	ID    uint   `gorm:"primaryKey"`
	Email string `gorm:"type:varchar(255);uniqueIndex"`
	Age   uint8  `gorm:"index;check:age >= 18 AND email is not null"`

	//复合索引
	FirstName string `gorm:"index:name"` //name的索引关联了两个字段0
	LastName  string `gorm:"index:name"`

	//顺序关键顺序
	//默认的 priority:10
	FirstName1 string `gorm:"index:name1,priority:2"` //排序先last后first
	LastName1  string `gorm:"index:name,priority:1"`

	//索引选项,前缀长度,排序方式,comment
	Height      float32 `gorm:"index:,sort:desc"`
	AddressHash string  `gorm:"index:,length:12,comment:前12个字符作为索引关键字"`
}

2.7 字段操作控制

请添加图片描述

请添加图片描述

type Service struct {
	gorm.Model
	//Url  `gorm:"-"` //该字段忽略  -:all 全部忽略
	Url         string `gorm:"-:migration"` //仅忽略迁移,可以做增删改查工作
	Schema      string
	Host        string
	Path        string
	QueryString string
}

2.8 序列化器的使用

请添加图片描述

type Paper struct {
	gorm.Model
	Subject string
	//Tags    []string 不可以 unsupported data type: &[]
	//使用 json 序列化器进行处理
	Tags []string `gorm:"serializer:json"`
}

func PaperCrud() {
	err := DB.AutoMigrate(&Paper{})
	if err != nil {
		log.Fatal(err)
	}
	//常规操作
	paper := &Paper{}
	paper.Subject = "使用serializer操作Tags字段"
	paper.Tags = []string{"GO", "Serializer", "Gorm", "MySQL"}
	//create 执行 序列化
	if err := DB.Create(paper).Error; err != nil {
		log.Fatal(err)
	}
	//查询执行 反序列化
	newPaper := &Paper{}
	DB.First(newPaper, 2)
	fmt.Printf("%+v", newPaper)
}

2.9 自定义序列化器的实现

请添加图片描述
请添加图片描述

type Paper struct {
	gorm.Model
	Subject string
	//Tags    []string 不可以 unsupported data type: &[]
	//使用 json 序列化器进行处理
	Tags       []string `gorm:"serializer:json"`
	Categories []string `gorm:"serializer:csv"`
}
// 1.定义实现了序列号器接口的类型
type CSVSerializer struct{}

// 实现Scan unserialize 反序列化
/*
ctx Context对象
field 模型的字段对应的类型
dst 目标值(最终结果赋值到dst)
dbValue 从数据库读取的值
*/
func (CSVSerializer) Scan(ctx context.Context, field *schema.Field, dst reflect.Value, dbValue interface{}) error {
	//初始化一个用来存储字段值的变量
	var fieldValue []string
	//一、解析读取到的数据表的数据
	if dbValue != nil { //不是NULL
		//支持解析的只有string和[]byte
		//使用类型检测进行判定
		var str string
		switch v := dbValue.(type) {
		case string:
			str = v
		case []byte:
			str = string(v)
		default:
			return fmt.Errorf("failed to unmarshal CSV value :%#v", dbValue)
		}
		//二、核心,将数据表中的字段使用逗号分割,形成[]string
		fieldValue = strings.Split(str, ",")
	}
	//三、将处理好的数据设置在dst上
	field.ReflectValueOf(ctx, dst).Set(reflect.ValueOf(fieldValue))
	return nil
}

// 实现Value serialize 序列化
// fieldValue 模型的字段值
func (CSVSerializer) Value(ctx context.Context, field *schema.Field, dst reflect.Value, fieldValue interface{}) (interface{}, error) {
	//将字段值转换为可存储的CSV结构
	return strings.Join(fieldValue.([]string), ","), nil
}

// 2.注册到GORM中
func CustomSerializer() {
	//	注册序列化器
	schema.RegisterSerializer("csv", CSVSerializer{})
	//3.测试
	err := DB.AutoMigrate(&Paper{})
	if err != nil {
		log.Fatal(err)
	}
	//常规操作
	paper := &Paper{}
	paper.Subject = "使用自定义操作categories字段"
	paper.Categories = []string{"GO", "Serializer", "Gorm", "MySQL", "categories "}
	paper.Tags = []string{"GO", "Serializer", "Gorm", "MySQL"}
	//create 执行 序列化
	if err := DB.Create(paper).Error; err != nil {
		log.Fatal(err)
	}
	//查询执行 反序列化
	newPaper := &Paper{}
	DB.First(newPaper, paper.ID)
	fmt.Printf("%+v", newPaper)
}

在这里插入图片描述

2.10 嵌入结构体和gorm.model

// 用于DB表交互的模型
type Blog struct {
	gorm.Model
	BlogBasic
	Author `gorm:"embeddedPrefix:au thor_"`
}
type BlogBasic struct {
	Subject string
	Summary string
	Content string
}
type Author struct {
	Name  string
	Email string
}

2.10 小节

请添加图片描述

3 具体操作

请添加图片描述
请添加图片描述

3.1

3.2 错误处理

请添加图片描述

请添加图片描述

3.3 基于模型和map完成创建

请添加图片描述
请添加图片描述

type Content struct {
	gorm.Model
	Subject     string
	Likes       uint
	PublishTime *time.Time
}
func CreateBasic() {
	DB.AutoMigrate(&Content{})
	//模型映射记录,操作模型字段,就是操作记录的列
	c1 := Content{}
	c1.Subject = "GORM的使用"
	//执行新增
	result := DB.Create(&c1)
	//处理错误
	if result.Error != nil {
		log.Fatal(result.Error)
	}
	//最新id,影响到记录数
	fmt.Println(c1.ID, result.RowsAffected)

	//通过 map 指定数据 新增记录
	values := map[string]any{
		"Subject":     "通过map指定的值",
		"PublishTime": time.Now(),
	}
	result2 := DB.Model(&Content{}).Create(values)
	if result2.Error != nil {
		log.Fatal(result2.Error)
	}
	//这个为什么输出不了id 原因:因为没有对模型进行创建,id不知道存放在哪里
	fmt.Println(result2.RowsAffected)
}

  • 使用map 自动更新创建时间 不会随之添加,只会在记录中添加map中有的数据
    在这里插入图片描述

3.4 批量插入

请添加图片描述

func CreateMulti() {
	DB.AutoMigrate(&Content{})
	//定义模型的切片
	models := []Content{
		{Subject: "标题1"},
		{Subject: "标题2"},
		{Subject: "标题3"},
		{Subject: "标题3-1"},
	}
	result := DB.Create(&models)
	if result.Error != nil {
		log.Fatal(result.Error)
	}
	fmt.Println("RowsAffected:", result.RowsAffected)
	for _, m := range models {
		fmt.Println("ID:", m.ID)
	}
	//切片结构同样支持
	vs := []map[string]any{
		{"Subject": "标题4"},
		{"Subject": "标题5"},
		{"Subject": "标题6"},
		{"Subject": "标题6-1"},
	}
	result2 := DB.Model(&Content{}).Create(vs)
	if result2.Error != nil {
		log.Fatal(result2.Error)
	}
	fmt.Println("ROWS:", result2.RowsAffected)
}}

请添加图片描述
请添加图片描述


// 防止sql语句过长
func CreateBatches() {
	DB.AutoMigrate(&Content{})
	//定义模型的切片
	models := []Content{
		{Subject: "标题11"},
		{Subject: "标题12"},
		{Subject: "标题13"},
		{Subject: "标题14"},
	}
	result := DB.CreateInBatches(&models, 2)
	if result.Error != nil {
		log.Fatal(result.Error)
	}
	fmt.Println("RowsAffected:", result.RowsAffected)
	for _, m := range models {
		fmt.Println("ID:", m.ID)
	}
	//切片结构同样支持
	vs := []map[string]any{
		{"Subject": "标题15"},
		{"Subject": "标题16"},
		{"Subject": "标题17"},
		{"Subject": "标题18"},
	}
	result2 := DB.Model(&Content{}).CreateInBatches(vs, 2)
	if result2.Error != nil {
		log.Fatal(result2.Error)
	}
	fmt.Println("ROWS:", result2.RowsAffected)
}

请添加图片描述

3.5 UpSert更新插入

请添加图片描述

func UpSert() {
	DB.AutoMigrate(&Content{})
	c1 := Content{}
	c1.Subject = "新标题1111"
	c1.Likes = 10
	DB.Create(&c1)

	c2 := Content{}
	c2.ID = c1.ID //冲突原因 主键重复
	c2.Subject = "新标题*****"
	c2.Likes = 200
	DB.Create(&c1)
	fmt.Println(c1)
	//冲突时更新全部字段
	if err := DB.
		Clauses(clause.OnConflict{UpdateAll: true}).
		Create(&c2).Error; err != nil {
		log.Fatal(err)
	}
	//冲突后,更新部分字段
	c4 := Content{}
	c4.ID = c1.ID            //冲突原因 主键重复
	c4.Subject = "新标题456789" //c4 "新标题*****"  300
	c4.Likes = 300
	if err := DB.Clauses(clause.OnConflict{DoUpdates: clause.AssignmentColumns(
		[]string{"likes"})}). //只更新likes字段
		Create(&c4).
		Error; err != nil {
		log.Fatal(err)
	}
}

3.6 创建时默认值的处理

请添加图片描述

type Content struct {
	gorm.Model
	Subject     string
	Likes       uint  `gorm:"default:99"`
	Vikes       *uint `gorm:"default:99"`
	PublishTime *time.Time
}
func DefaultValue() {
	DB.AutoMigrate(&Content{})
	c1 := Content{}
	c1.Subject = "新标题2222"
	//下面两行 vikes字段输入数据库值为 0,而不是99,因为Vikes字段零值是 nil,而uint(0)有地址
	vlikes := uint(0)
	c1.Vikes = &vlikes
	DB.Create(&c1)
}

在这里插入图片描述

请添加图片描述

3.7 创建时选择特定字段

请添加图片描述

func  Omit() {
	DB.AutoMigrate(&Content{})

	c1 := Content{}
	c1.Subject = "原始标题"
	c1.Likes = 10
	c1.Vikes = 99
	now := time.Now()
	c1.PublishTime = &now
	//需要操作的字段有 Subject Likes UpdatedAt
	DB.Select("Subject", "Likes", "UpdatedAt").Create(&c1)
	//不需要操作的字段有 Subject UpdatedAt Vikes
	DB.Omit("Subject", "UpdatedAt", "Vikes").Create(&c1)
}

3.8 创建时的钩子方法

请添加图片描述

func (c *Content) BeforeCreate(db *gorm.DB) error {
	//业务
	if c.PublishTime == nil {
		now := time.Now()
		c.PublishTime = &now
	}
	//配置
	db.Statement.AddClause(clause.OnConflict{UpdateAll: true})
	return nil
}

请添加图片描述

请添加图片描述

请添加图片描述

3.9 查询 基于主键的查询

请添加图片描述
请添加图片描述

type Content struct {
	gorm.Model
	Subject     string
	Likes       uint `gorm:`
	Vikes       uint `gorm:`
	PublishTime *time.Time
}
type ContentStrPK struct {
	ID          string `gorm:"primaryKey"`
	Subject     string
	Likes       uint
	Views       uint
	PublishTime *time.Time
}
func GetByPk() {
	//migrate
	DB.AutoMigrate(&Content{}, &ContentStrPK{})
	//查询单条
	c := Content{}
	if err := DB.First(&c, 10).Error; err != nil {
		log.Println(err)
	}
	//字符串类型的主键
	cStr := ContentStrPK{}
	if err := DB.First(&cStr, "id = ?", "some pk").Error; err != nil {
		log.Println(err)
	}
	//查询多条
	var cs []Content
	if err := DB.Find(&cs, []uint{10, 11, 12, 13}).Error; err != nil {
		log.Println(err)
	}
	for _, v := range cs {
		fmt.Println(v)
	}
	//字符串类型的主键
	var cStrs []ContentStrPK
	if err := DB.Find(&cStrs, "id IN ?", []string{"some", "key", "you"}).Error; err != nil {
		log.Println(err)
	}
}

3.10 查询单条

请添加图片描述

func GetONE() {
	c := Content{}
	if err := DB.First(&c, "id > ?", 19).Error; err != nil {
		log.Println(err)
	}
	fmt.Println("first ", c)
	l := Content{}
	if err := DB.Last(&l, "id > ?", 19).Error; err != nil {
		log.Println(err)
	}
	fmt.Println("Last ", l)
	t := Content{}
	if err := DB.Take(&t, "id > ?", 19).Error; err != nil {
		log.Println(err)
	}
	fmt.Println("Take ", t)
	f := Content{}
	if err := DB.Limit(1).Find(&f, "id > ?", 19).Error; err != nil {
		fmt.Println(err)
	}
	fmt.Println("Limit Find ", f)
	fs := Content{}
	if err := DB.Find(&fs, "id > ?", 19).Error; err != nil {
		fmt.Println(err)
	}
	fmt.Println("find ", fs)

}

3.11 查询结果扫描到Map类型

请添加图片描述
请添加图片描述

func GetToMap() {
	//单条
	c := map[string]any{} //map[string]interface{}{}
	if err := DB.Model(&Content{}).First(&c, 13).Error; err != nil {
		log.Println(err)
	}
	fmt.Println(c["id"])
	if c["id"].(uint) == 13 { //断言
		fmt.Println("BING GO!")
	}
	//time类型的处理
	fmt.Println(c["created_at"])
	t, err := time.Parse("2006-01-02 15:04:05.000 -0700 CST", "2023-08-24 20:46:20.372 +0800 CST")
	if err != nil {
		log.Println(err)
	}
	if c["created_at"].(time.Time) == t {
		fmt.Println("created_at time bingo !")
	}
	//多条
	var cs []map[string]any
	if err := DB.Model(&Content{}).Find(&cs, []uint{18, 19, 20}).Error; err != nil {
		fmt.Println(err)
	}
	for _, v := range cs {
		fmt.Println(v["id"], v["subject"])
	}
}

3.12 查询单列

请添加图片描述

// 单个列
func GetPluck() {
	//使用切片存储
	var subjects []sql.NullString
	if err := DB.Model(Content{}).Pluck("subject", &subjects).Error; err != nil {
		fmt.Println(err)
	}
	for _, v := range subjects {
		if v.Valid {
			fmt.Println(v.String)
		} else if v.Valid == false {
			fmt.Println("{NULL}", v.Valid)
		}
	}
}

// 两个列连接
func GetPluckExp() {
	//使用切片存储,如果表达式可以保证null不会出现,就可以不使用nulltype
	var subjects []string
	//字段为表达式的结果
	if err := DB.Model(&Content{}).
		Pluck("concat(coalesce(subject,'[no subject]'),'-',likes)", &subjects).Error; err != nil {
		log.Println(err)
	}
	for _, v := range subjects {
		fmt.Println(v)
	}
}

3.13 字段选择子句

type Content struct {
	gorm.Model
	Subject     string
	Likes       uint `gorm:`
	Vikes       uint `gorm:`
	PublishTime *time.Time
	//不需要迁移
	//禁用写操作
	Sv string `gorm:"-:migration;<-:false;"`
}

func GetSelect() {
	var c Content
	var cm map[string]any
	//一、基本字段名
	if err := DB.Select("subject", "likes").First(&c, 13).Error; err != nil {
		log.Fatalln(err)
	}
	//二、字段表达式
	//1.映射至结构体 需要定义结构体字段 Sv
	if err := DB.Select("subject", "likes", "concat(subject,'-',vikes) AS sv").First(&c, 13).Error; err != nil {
		log.Fatalln(err)
	}
	//2.映射至map map不需要定义结构体的字段 Sv
	if err := DB.Model(&Content{}).Select("subject", "likes", "concat(subject,'-',vikes) AS sv").First(&cm, 13).Error; err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("%+v\n", cm)
}

3.14 去重选项distinct

请添加图片描述


func GetDistinct() {
	var cs []Content
	//基本字段名
	if err := DB.Distinct("subject").Find(&cs).Error; err != nil { //Distinct第一个参数一般为 "*"(去重字段)
		log.Fatalln(err)
	}
	for _, v := range cs {
		fmt.Printf("%+v\n", v.Subject)
	}

}

3.15 条件设置方法

请添加图片描述

func WhereMethod() {
	var cs []Content
	//1. inline条件,内联条件
	if err := DB.Find(&cs, "likes > ? AND subject like ?", 90, "新标题%").Error; err != nil {
		log.Fatalln(err)
	}
	
	//2.Where,通常在动态拼凑条件时使用 等同于 1
	if err := DB.Where("likes > ? AND subject like ?", 90, "新标题%").Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}
	query := DB.Where("likes > ?", 90)
	subject := "123"
	if subject != "" { //当前用户输入subject,不为空字符串时,才拼凑subject条件
		query.Where("subject like ?", "新标题%")
	}
	
	//3. OR 逻辑运算
	query = DB.Where("likes > ?", 90)
	subject = "新标题"
	if subject != "" { //当前用户输入subject,不为空字符串时,才拼凑subject条件
		//query.Where("subject like ?", subject+"%")
		query.Or("subject like ?", subject+"%")
	}
	
	//4.Not 逻辑运算
	query = DB.Where("likes > ?", 90)
	subject = "新标题"
	if subject != "" { //当前用户输入subject,不为空字符串时,才拼凑subject条件
		//query.Where("subject like ?", subject+"%")
		query.Not("subject like ?", subject+"%")
		/*SELECT * FROM `msb_content` WHERE
		(likes > 90 OR NOT subject like '新标题%')
		AND `msb_content`.`deleted_at` IS NULL
		*/
		query.Or(DB.Not("subject like ?", subject+"%"))
		
	}
	if err := query.Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}
}

3.16 条件语法规则

请添加图片描述

func WhereType() {
	var cs []Content
	//一、(1 or 2) and (3 and (4 or 5))
	condA := DB.Where("subject < ?", 1).Or("subject > ?", 2)
	condB := DB.Where("subject < ?", 3).Where(DB.Where("subject < ?", 4).Or("subject < ?", 5))
	query := DB.Where(condA).Where(condB)
	//SELECT * FROM `msb_content` WHERE (subject < 1 OR subject > 2)
	//AND
	//(subject < 3 AND (subject < 4 OR subject < 5)) AND `msb_content`.`deleted_at` IS NULL

	//二、map构建条件,and,判断 =,in
	query = DB.Where(map[string]any{
		"views": 100,
		"id":    []uint{1, 2, 3, 4, 5},
	})

	//三、struct条件构建
	query = DB.Where(Content{
		Subject: "GORM",
		Vikes:   100,
	})
	if err := query.Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}

}

请添加图片描述

func PlaceHolder() {
	var cs []Content
	//一、匿名
	query := DB.Where("likes = ? AND subject like ?", 100, "gorm%")
	//二、2.1 具名,绑定名字sql.Named()结构
	query = DB.Where("likes = @like AND subject like @subject",
		sql.Named("subject", "gorm%"), sql.Named("like", 100))
	//2.2、gorm还支持使用map的形式具名绑定
	query = DB.Where("likes = @like AND subject like @subject",
		map[string]any{
			"subject": "gorm%",
			"like":    100,
		})
	if err := query.Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}
}

3.17 字段排序和表达式排序

请添加图片描述

func OrderBy() {
	var cs []Content
	ids := []uint{2, 3, 1}
	//1.
	query := DB.Order("FIELD(id,2,3,1)") //按2 3 1 顺序输出
	//2.
	query = DB.Clauses(clause.OrderBy{
		Expression: clause.Expr{
			SQL:                "FIELD(id,?)",
			Vars:               []any{ids},
			WithoutParentheses: true,
		},
	})
	if err := query.Find(&cs, ids).Error; err != nil {
		log.Fatalln(err)
	}
	for _, c := range cs {
		fmt.Println(c.ID)
	}

}

3.18 结果限定及分页

请添加图片描述

// 定义分页必要的数据结构
// 默认的值
const (
	DefaultPage     = 1
	DefaultPageSize = 12
)

// 定义分页必要的数据结构
type Pager struct {
	Page, PageSize int
}

// 翻页程序
func Pagination(pager Pager) {
	//确定 offset 和 pagesize
	page := DefaultPage
	if pager.Page != 0 {
		page = pager.Page
	}
	pagesize := DefaultPageSize
	if pager.PageSize != 0 {
		pagesize = pager.PageSize
	}
	//计算 offset
	//page pagesize offset
	//1 10 0
	//2 10 10
	//3 10 20
	offset := pagesize * (page - 1)
	var cs []Content
	if err := DB.Offset(offset).Limit(pagesize).Find(&cs); err != nil { //偏移量,页大小
		log.Println(err)
	}
	for _, v := range cs {
		fmt.Println(v.ID)
	}
}

请添加图片描述

// 用于得到func(db *gorm.DB) *gorm.DB类型函数
// 为什么不直接定义函数,因为需要func(db *gorm.DB) *gorm.DB与分页产生联系
func Paginate(pager Pager) func(db *gorm.DB) *gorm.DB {
	page := DefaultPage
	if pager.Page != 0 {
		page = pager.Page
	}
	pagesize := DefaultPageSize
	if pager.PageSize != 0 {
		pagesize = pager.PageSize
	}
	offset := pagesize * (page - 1)

	return func(db *gorm.DB) *gorm.DB {
		//使用闭包的变量,实现翻页的业务逻辑
		return db.Offset(offset).Limit(pagesize)
	}
}

// 测试重用代码
func PaginationScope(pager Pager) {
	var cs []Content
	if err := DB.Scopes(Paginate(pager)).Find(&cs).Error; err != nil {
		log.Println(err)
	}
	for _, v := range cs {
		fmt.Println(v.ID)
	}
}

3.19 分组聚合过滤

请添加图片描述

func GroupHaving() {
	DB.AutoMigrate(&Content{})
	//定义查询结构类型
	type Result struct {
		//分组字段
		AuthorID uint
		//合计字段
		TotalViews int
		TotalLikes int
		AvgViews   float64
	}
	//执行分组合计过滤查询
	var re []Result
	if err := DB.Model(&Content{}).
		//SELECT `author_id`,SUM(vikes) as total_views,SUM(likes) as total_likes,AVG(vikes) as avg_views
		//FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL
		//GROUP BY `author_id` HAVING total_views > 99
		Select("author_id", "SUM(vikes) as total_views", "SUM(likes) as total_likes", "AVG(vikes) as avg_views").
		Group("author_id").Having("total_views > ?", 99).
		Find(&re).Error; err != nil {
		log.Fatalln(err)
	}
	//SQL
}

3.20 count合计

请添加图片描述

func Count(pager Pager) {
	//集中的条件,用于统计数量和获取某页记录
	query := DB.Model(&Content{}).
		Where("likes > ?", 99)
	//total rows count
	var count int64
	if err := query.Count(&count).Error; err != nil {
		log.Fatalln(err)
	}
	// 计算总页数 ceil(count / pagesize)
	//row per page
	var cs []Content
	if err := query.Scopes(Paginate(pager)).Find(&cs).Error; err != nil {
		log.Println(err)
	}
	for _, v := range cs {
		fmt.Println(v.ID)
	}
}

3.21 迭代查询

请添加图片描述

func Iterator() {
	//利用DB.Rows() 获取 Rows对象
	rows, err := DB.Model(&Content{}).Rows()
	if err != nil {
		log.Fatalln(err)
	}
	//注意:保证使用过后关闭rows结果集
	defer func() {
		_ = rows.Close()
	}()
	//迭代的从Rows中扫描记录到模型
	for rows.Next() {
		//还有记录存在于结果集中
		var c Content
		if err := DB.ScanRows(rows, &c); err != nil {
			log.Fatalln(err)
		}
		fmt.Println(c.Subject)
	}
}

3.22 锁子句

请添加图片描述

func Locking() {
	var cs []Content
	if err := DB.
		Clauses(clause.Locking{Strength: "UPDATE"}).
		Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}
	if err := DB.
		Clauses(clause.Locking{Strength: "SHARE"}).
		Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}
}

3.23 子查询

请添加图片描述

func SubQuery() {
	//migrate
	DB.AutoMigrate(&Author{}, &Content{})
	//条件型子查询
	//select * from content where author_id in (select id from author where status=0);
	//子查询,不需要使用终结方法Find完成查询,只需要构建语句即可
	whereSubQuery := DB.Model(&Author{}).Select("id").Where("status = ?", 0)
	var cs []Content
	if err := DB.Where("author_id IN (?)", whereSubQuery).Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}

	//from型子查询
	//select * from (select subject,likes from content where publish_time is null) as temp where likes > 10);
	fromSubQuery := DB.Model(&Content{}).Select("subject", "likes").Where("publish_time IS NULL")
	type Result struct {
		Subject string
		Likes   int
	}
	var rs []Result
	if err := DB.Table("(?) AS temp", fromSubQuery).
		Where("likes > ?", 10).
		Find(&rs).Error; err != nil {
		log.Fatalln(err)
	}
}

3.24 钩子方法 查询钩子

请添加图片描述

func (c *Content) AfterFind(db *gorm.DB) error {
	if c.AuthorID == 0 {
		c.AuthorID = 1 //1.假定的默认作者
	}
	return nil
}

func FindHook() {
	var c Content
	if err := DB.First(&c, 13).Error; err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("%+v\n", c)
}
  • AuthorID值赋为1
    在这里插入图片描述

3.25 更新-主键条件更新及获取更新记录数

请添加图片描述

func UpdatePK() {
	var c Content
	//无主键
	if err := DB.Save(&c).Error; err != nil {
		log.Fatalln(err)
	}
	fmt.Println("1:", c.ID)
	//具有主键id
	if err := DB.Save(&c).Error; err != nil {
		log.Fatalln(err)
	}
	fmt.Println("2:", c.ID)
}

请添加图片描述

请添加图片描述

func UpdateWhere() {
	// 更新的字段值数据
	// map推荐
	values := map[string]any{
		"subject": "Where Update Row",
		"likes":   10001,
	}
	//执行带有条件的更新
	result := DB.Model(&Content{}).
		Omit("updated_at"). //操作时 忽略该字段
		Where("likes > ?", 100).
		Updates(values)
	if result.Error != nil {
		log.Fatalln(result.Error)
	}
	//获取更新结果,更新的记录数量(受影响的记录数)
	//修改的记录数,而不是满足条件的记录数
	log.Println("updated rows num: ", result.RowsAffected)
}

请添加图片描述

请添加图片描述
请添加图片描述

请添加图片描述

func UpdateNOWhere() {
	// 更新的字段值数据
	// map推荐
	values := map[string]any{
		"subject": "Where Update Row",
		"likes":   101,
	}
	//执行带有条件的更新
	result := DB.Model(&Content{}).
		Omit("updated_at"). //操作时 忽略该字段
		Where("1").
		Updates(values)
	if result.Error != nil {
		log.Fatalln(result.Error)
	}
	//获取更新结果,更新的记录数量(受影响的记录数)
	//修改的记录数,而不是满足条件的记录数
	log.Println("updated rows num: ", result.RowsAffected)
}

3.26 更新-表达式值

请添加图片描述
请添加图片描述

func UpdateExpr() {
	// 更新的字段值数据
	// map推荐
	values := map[string]any{
		"subject": "Where Update Row",
		//值为表达式计算的结果时,使用Expr类型
		"likes": gorm.Expr("likes + ?", 10),
	}
	//执行带有条件的更新
	result := DB.Model(&Content{}).
		Omit("updated_at"). //操作时 忽略该字段
		Where("likes > ?", 100).
		Updates(values)
	if result.Error != nil {
		log.Fatalln(result.Error)
	}
	//获取更新结果,更新的记录数量(受影响的记录数)
	//修改的记录数,而不是满足条件的记录数
	log.Println("updated rows num: ", result.RowsAffected)
}

3.27 更新-Hook简单介绍

请添加图片描述

3.28 删除-基本主键条件删除操作

请添加图片描述

请添加图片描述

func DeleteWhere() {
	result1 := DB.Delete(&Content{}, "likes < ?", 100)
	if err := result1.Error; err != nil {
		log.Fatalln(result1.Error)
	}
	result2 := DB.Where("likes < ?", 100).Delete(&Content{})
	if err := result2.Error; err != nil {
		log.Fatalln(err)
	}
}

请添加图片描述

3.29 删除-软删除和永久删除

请添加图片描述
请添加图片描述
请添加图片描述

func FindDeleted() {
	var c Content
	DB.Delete(&c, 13)
	//if err := DB.First(&c, 13).Error; err != nil {
	//	log.Fatalln(err)
	//}
	//fmt.Println(c)
	if err := DB.Unscoped().First(&c, 13).Error; err != nil {
		log.Fatalln(err)
	}
	/*
		unscoped: {{13 2023-08-24 20:46:20.372 +0800 CST 2023-08-24 20:46:20.372 +0800 CST {2023-08-28 21:38:25.085 +0800 CST true}} 标题14 0 99 2023-08-24 17:12:56.788
		+0800 CST  1}
	*/
	fmt.Println("unscoped:", c)
}

请添加图片描述

func DeleteHard() {
	var c Content
	if err := DB.Unscoped().Delete(&c, 14).Error; err != nil {
		log.Fatalln(err)
	}
}

3.30 原生sql的执行和结果处理

请添加图片描述

// 原生查询测试
func RawSelect() {
	// 结果类型
	type Result struct {
		ID           uint
		Subject      string
		Likes, Vikes int
	}
	var rs []Result
	//SQL
	sql := "SELECT `id`,`subject`,`likes`,`vikes` FROM `msb_content` WHERE `likes` > ? ORDER BY `likes` DESC LIMIT ?"
	//执行SQL,并扫描结果
	if err := DB.Raw(sql, 99, 12).Scan(&rs).Error; err != nil {
		log.Fatalln(err)
	}
	log.Println(rs)
}

// 执行类的SQL原生
func RawExec() {
	//SQL
	sql := "UPDATE `msb_content` SET `subject` = CONCAT(`subject`,'-new postfix') WHERE `id` BETWEEN ? AND ?"
	//执行,获取结果
	result := DB.Exec(sql, 20, 30)
	if result.Error != nil {
		log.Fatalln(result.Error)
	}
	log.Println(result.RowsAffected)
}

请添加图片描述


// sql.Row 或 sql.Rows 类型的结果处理
func RowAndRows() {
	//SQL
	sql := "SELECT `id`,`subject`,`likes`,`vikes` FROM `msb_content` WHERE `likes` > ? ORDER BY `likes` DESC LIMIT ?"
	//执行,获取rows
	rows, err := DB.Raw(sql, 99, 12).Rows()
	if err != nil {
		log.Fatalln(err)
	}
	//遍历rows
	for rows.Next() {
		//扫描的列独立的变量
		//var id uint
		//var subject string
		//var likes, vikes int
		//rows.Scan(&id, &subject, &likes, &vikes)
		//fmt.Println(id, subject, likes, vikes)
		//扫描到整体结构体
		type Result struct {
			ID           uint
			Subject      string
			Likes, Vikes int
		}
		var r Result
		DB.ScanRows(rows, &r)
		fmt.Println(r)
	}

}

3.31 会话模式的基本使用

请添加图片描述

func SessionIssue() {
	db := DB.Model(&Content{}).Where("vikes > ?", 100)
	db.Where("likes > ?", 99)
	//SELECT * FROM `msb_content` WHERE vikes > 100 AND likes > 99 AND `msb_content`.`deleted_at` IS NULL
	var cs []Content
	db.Find(&cs)
}

请添加图片描述

func SessionNew() {
	//	需要重复使用的部分
	//	将Session方法前的配置,记录到了当前的会话中
	//	后边再次调用db方法直到终结方法,会保持会话中的子句选项
	//	执行完终结方法后,再次调用db的方法,可以重用
	db := DB.Model(&Content{}).Where("vikes > ?", 100).Session(&gorm.Session{})
	var cs1 []Content
	// SELECT * FROM `msb_content` WHERE vikes > 100 AND likes > 9 AND `msb_content`.`deleted_at` IS NULL
	db.Where("likes > ?", 9).Find(&cs1)
	var cs2 []Content
	// SELECT * FROM `msb_content` WHERE vikes > 100 AND likes > 99 AND `msb_content`.`deleted_at` IS NULL
	db.Where("likes > ?", 99).Find(&cs2)
}

3.32 会话模式的常用实操选项

请添加图片描述
请添加图片描述
请添加图片描述

请添加图片描述

请添加图片描述

func SessionOptions() {
	// Skip Hook
	db:=DB.Session(&gorm.Session{
		SkipHooks:true,
	})
	db.Save(&Content{Subject: "no create hook"})

	// Dry Run 生成SQL,但不执行
	db := DB.Session(&gorm.Session{
		DryRun: true,
	})
	stmt := db.Save(&Content{}).Statement
	fmt.Println(stmt.SQL.String())
	fmt.Println(stmt.Vars)

	//prepare 预编译 编译重用 编译独立
	//SQL 编译 绑定数据 执行
	//预编译,将编译的过程缓存起来,便于重用
	//在执行结构相同的SQL时,可以重用。
	db := DB.Session(&gorm.Session{
		PrepareStmt: true,
	})
}

3.33 context上下文支持

请添加图片描述

func ContextTimeoutCancel() {
	// 设置一个定时Cancel的Context  执行时间超过time.Millisecond会取消操作
	ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
	defer cancel()
	// 传递Context执行
	var cs []Content
	if err := DB.WithContext(ctx).Limit(5).Find(&cs).Error; err != nil {
		log.Fatalln(err)
	}
	fmt.Println(cs)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值