package gormExample import ( "database/sql" "fmt" "gorm.io/gorm" "gorm.io/gorm/clause" "log" "time" ) func GetByPk() { // migrate DB.AutoMigrate(&Content{}, &ContentStrPK{}) // 查询单条 ////c := Content{} ////if err := DB.First(&c, 10).Error; err != nil { //// log.Println(err) ////} //c := Content{} //c.ID = 10 //if err := DB.First(&c).Error; err != nil { // log.Println(err) //} //// 字符串类型的主键 //cStr := ContentStrPK{} //if err := DB.First(&cStr, "id = ?", "some pk").Error; err != nil { // //if err := DB.First(&cStr, "some pk").Error; err != nil { // log.Println(err) //} // //// 查询多条 //var cs []Content //if err := DB.Find(&cs, []uint{10, 11, 12}).Error; err != nil { // log.Println(err) //} // 字符串类型的主键 var cStrs []ContentStrPK if err := DB.Find(&cStrs, []string{"some", "pk", "item"}).Error; err != nil { log.Println(err) } } func GetOne() { c := Content{} if err := DB.First(&c, "id > ?", 42).Error; err != nil { log.Println(err) } o := Content{} if err := DB.Last(&o, "id > ?", 42).Error; err != nil { log.Println(err) } n := Content{} if err := DB.Take(&n, "id > ?", 42).Error; err != nil { log.Println(err) } f := Content{} if err := DB.Limit(1).Find(&f, "id > ?", 42).Error; err != nil { log.Println(err) } fs := Content{} if err := DB.Find(&fs, "id > ?", 42).Error; err != nil { log.Println(err) } } 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"], c["id"].(uint) == 13) // 需要接口类型断言,才能继续处理 if c["id"].(uint) == 13 { fmt.Println("id bingo") } // time类型的处理 fmt.Println(c["created_at"]) t, err := time.Parse("2006-01-02 15:04:05.000 -0700 CST", "2023-04-10 22:00:11.582 +0800 CST") if err != nil { log.Println(err) } if c["created_at"].(time.Time) == t { fmt.Println("created_at bingo") } // 多条 var cs []map[string]any if err := DB.Model(&Content{}).Find(&cs, []uint{13, 14, 15}).Error; err != nil { log.Println(err) } for _, c := range cs { fmt.Println(c["id"].(uint), c["subject"].(string), c["created_at"].(time.Time)) } } func GetPluck() { // 使用切片存储 var subjects []sql.NullString //var subjects []string if err := DB.Model(&Content{}).Pluck("subject", &subjects).Error; err != nil { log.Println(err) } for _, subject := range subjects { //NullString的使用 if subject.Valid { fmt.Println(subject.String) } else { fmt.Println("[NULL]") } // //fmt.Println(subject) } } 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 _, subject := range subjects { fmt.Println(subject) } } func GetSelect() { var c Content // 基本的字段名 //if err := DB.Select("subject", "likes").First(&c, 13).Error; err != nil { // log.Fatalln(err) //} // 字段表达式 if err := DB.Select("subject", "likes", "concat(subject,'-', views) AS sv").First(&c, 13).Error; err != nil { log.Fatalln(err) } fmt.Printf("%+v\n", c) } func GetDistinct() { var cs []Content // 基本的字段名 if err := DB.Distinct("*").Find(&cs).Error; err != nil { log.Fatalln(err) } fmt.Printf("%+v\n", cs) } func WhereMethod() { var cs []Content // inline条件,内联条件 //if err := DB.Find(&cs, "likes > ? AND subject like ?", 100, "gorm%").Error; err != nil { // log.Fatalln(err) //} // SELECT * FROM `msb_content` WHERE (likes > 100 AND subject like 'gorm%') AND `msb_content`.`deleted_at` IS NULL // Where,通常在动态拼凑条件时使用 //query := DB.Where("likes > ?", 100) //subject := "" //// 当前用户输出subject,不为空字符串时,才拼凑subject条件 //if subject != "" { // query.Where("subject like ?", subject+"%") //} //if err := query.Find(&cs).Error; err != nil { // log.Fatalln(err) //} // SELECT * FROM `msb_content` WHERE likes > 100 AND `msb_content`.`deleted_at` IS NULL // OR 逻辑运算 //query := DB.Where("likes > ?", 100) //subject := "gorm" //// 当前用户输出subject,不为空字符串时,才拼凑subject条件 //if subject != "" { // //query.Where("subject like ?", "subject"+"%") // query.Or("subject like ?", subject+"%") //} //if err := query.Find(&cs).Error; err != nil { // log.Fatalln(err) //} // SELECT * FROM `msb_content` WHERE (likes > 100 OR subject like 'gorm%') AND `msb_content`.`deleted_at` IS NULL // Not 逻辑运算 query := DB.Where("likes > ?", 100) subject := "gorm" // 当前用户输出subject,不为空字符串时,才拼凑subject条件 if subject != "" { //query.Not("subject like ?", subject+"%") // SELECT * FROM `msb_content` WHERE likes > 100 AND NOT subject like 'gorm%' AND `msb_content`.`deleted_at` IS NULL query = query.Or(DB.Not("subject like ?", subject+"%")) // SELECT * FROM `msb_content` WHERE (likes > 100 OR NOT subject like 'gorm%') AND `msb_content`.`deleted_at` IS NULL } if err := query.Find(&cs).Error; err != nil { log.Fatalln(err) } } func WhereType() { var cs []Content // 嵌套分组构建复杂的逻辑运算 // (1 or 2) and (3 and (4 or 5)) // 1 or 2 //condA := DB.Where("likes > ?", 10).Or("likes <= ?", 100) // 3 and (4 or 5) //condB := DB.Where("views > ?", 20).Where(DB.Where("views <= ?", 200).Or("subject like ?", "gorm%")) //query := DB.Where(condA).Where(condB) // SELECT * FROM `msb_content` WHERE (likes > 10 OR likes <= 100) AND (views > 20 AND (views <= 200 OR subject like 'gorm%')) 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}, //}) // SELECT * FROM `msb_content` WHERE `id` IN (1,2,3,4,5) AND `views` = 100 AND `msb_content`.`deleted_at` IS NULL // struct条件构建, and, = query := DB.Where(Content{ Views: 100, Subject: "GORM", }) // SELECT * FROM `msb_content` WHERE `msb_content`.`subject` = 'GORM' AND `msb_content`.`views` = 100 AND `msb_content`.`deleted_at` IS NULL 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%") // 具名,绑定名字sql.Named()结构 //query := DB.Where("likes = @like AND subject like @subject", sql.Named("subject", "gorm%"), sql.Named("like", 100)) // SELECT * FROM `msb_content` WHERE (likes = 100 AND subject like 'gorm%') AND `msb_content`.`deleted_at` IS NULL // gorm还支持使用map的形式具名绑定 query := DB.Where("likes = @like AND subject like @subject", map[string]any{ "subject": "gorm%", "like": 100, }) // SELECT * FROM `msb_content` WHERE (likes = 100 AND subject like 'gorm%') AND `msb_content`.`deleted_at` IS NULL if err := query.Find(&cs).Error; err != nil { log.Fatalln(err) } } func OrderBy() { var cs []Content ids := []uint{2, 3, 1} //query := DB.Order("FIELD(id, 2, 3, 1)") query := DB.Clauses(clause.OrderBy{ Expression: clause.Expr{ SQL: "FIELD(id, ?)", Vars: []any{ids}, WithoutParentheses: true, }, }) // SELECT * FROM `msb_content` WHERE `msb_content`.`id` IN (2,3,1) AND `msb_content`.`deleted_at` IS NULL ORDER BY FIELD(id, 2,3,1) if err := query.Find(&cs, ids).Error; err != nil { log.Fatalln(err) } for _, c := range cs { fmt.Println(c.ID) } } // 定义分页必要数据结构 type Pager struct { Page, PageSize int } // 默认的值 const ( DefaultPage = 1 DefaultPageSize = 12 ) // 翻页程序 func Pagination(pager Pager) { // 确定page, 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 // SELECT * FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL LIMIT 15 OFFSET 30 if err := DB.Offset(offset).Limit(pagesize).Find(&cs).Error; err != nil { log.Fatalln(err) } } // 用于得到func(db *gorm.DB) *gorm.DB类型函数 // 为什么不直接定义函数,因为需要func(db *gorm.DB) *gorm.DB与分页信息产生联系。 func Paginate(pager Pager) func(db *gorm.DB) *gorm.DB { // 计算page page := DefaultPage if pager.Page != 0 { page = pager.Page } // 计算pagesize 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) return func(db *gorm.DB) *gorm.DB { // 使用闭包的变量,实现翻页的业务逻辑 return db.Offset(offset).Limit(pagesize) } } // 测试重用的分页查询 func PaginationScope(pager Pager) { var cs []Content // SELECT * FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL LIMIT 15 OFFSET 30 if err := DB.Scopes(Paginate(pager)).Find(&cs).Error; err != nil { log.Fatalln(err) } var ps []Post // SELECT * FROM `msb_post` WHERE `msb_content`.`deleted_at` IS NULL LIMIT 15 OFFSET 30 if err := DB.Scopes(Paginate(pager)).Find(&ps).Error; err != nil { log.Fatalln(err) } } 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) } // SELECT count(*) FROM `msb_content` WHERE likes > 99 AND `msb_content`.`deleted_at` IS NULL // 计算总页数 ceil( count / pagesize) // rows per page var cs []Content if err := query.Scopes(Paginate(pager)).Find(&cs).Error; err != nil { log.Fatalln(err) } // SELECT * FROM `msb_content` WHERE likes > 99 AND `msb_content`.`deleted_at` IS NULL LIMIT 15 OFFSET 30 } func GroupHaving() { DB.AutoMigrate(&Content{}) // 定义查询结构类型 type Result struct { // 分组字段 AuthorID uint // 合计字段 TotalViews int TotalLikes int AvgViews float64 } // 执行分组合计过滤查询 var rs []Result if err := DB.Model(&Content{}). Select("author_id", "SUM(views) as total_views", "SUM(likes) as total_likes", "AVG(views) as avg_views"). Group("author_id").Having("total_views > ?", 99). Find(&rs).Error; err != nil { log.Fatalln(err) } // SQL // SELECT `author_id`,SUM(views) as total_views,SUM(likes) as total_likes,AVG(views) as avg_views FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL GROUP BY `author_id` HAVING total_views > 99 } func Iterator() { // 利用DB.Rows() 获取Rows对象 rows, err := DB.Model(&Content{}).Rows() if err != nil { log.Fatalln(err) } // [rows:-] SELECT * FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL // 注意:保证使用过后关闭rows结果集 defer func() { _ = rows.Close() }() fmt.Println(rows) // 迭代的从Rows中扫描记录到模型 for rows.Next() { // 还有记录存在与结果集中 var c Content if err := DB.ScanRows(rows, &c); err != nil { log.Fatalln(err) } fmt.Println(c.Subject) } } func Locking() { var cs []Content if err := DB. Clauses(clause.Locking{Strength: "UPDATE"}). Find(&cs).Error; err != nil { log.Fatalln(err) } // [4.998ms] [rows:42] SELECT * FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL FOR UPDATE if err := DB. Clauses(clause.Locking{Strength: "SHARE"}). Find(&cs).Error; err != nil { log.Fatalln(err) } // [3.006ms] [rows:42] SELECT * FROM `msb_content` WHERE `msb_content`.`deleted_at` IS NULL FOR SHARE } 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) } // [4.782ms] [rows:0] SELECT * FROM `msb_content` WHERE author_id IN (SELECT `id` FROM `msb_author` WHERE status = 0 AND `msb_author`.`deleted_at` IS NULL) AND `msb_content`.`deleted_at` IS NULL // from型子查询 //select * from (select subject, likes from content where publish_time is null) as temp where likes > 10; fromSubQuery := DB.Model(&Content{}).Where("publish_time IS NULL").Select("subject", "likes") 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.800ms] [rows:17] SELECT * FROM (SELECT `subject`,`likes` FROM `msb_content` WHERE publish_time IS NULL AND `msb_content`.`deleted_at` IS NULL) AS temp WHERE likes > 10 } func FindHook() { var c Content if err := DB.First(&c, 13).Error; err != nil { log.Fatalln(err) } fmt.Printf("%+v\n", c) }