You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

508 lines
13 KiB

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)
}