当前位置: 首页 > news >正文

GORM 基础 -- CRUD 接口

1、Create

1.1 创建纪录

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}result := db.Create(&user) // pass pointer of data to Createuser.ID             // 回填插入数据的主键
result.Error        // 返回的 error 信息
result.RowsAffected // 返回插入记录的个数

1.2 用选定的字段创建记录

创建一个记录并为指定的字段分配值

db.Select("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`name`,`age`,`created_at`) VALUES ("jinzhu", 18, "2020-07-04 11:05:21.775")

创建一个记录并忽略传递给要忽略的字段的值。

db.Omit("Name", "Age", "CreatedAt").Create(&user)
// INSERT INTO `users` (`birthday`,`updated_at`) VALUES ("2020-01-01 00:00:00.000", "2020-07-04 11:05:21.775")

1.3 Batch Insert

为了有效地插入大量的记录,将一个切片传递给Create方法。GORM将生成一条SQL语句来插入所有数据并回填主键值,钩子方法(hook methods)也将被调用。

var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)for _, user := range users {user.ID // 1,2,3
}

你可以在创建CreateInBatches时指定批大小,例如:

var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}// batch size 100
db.CreateInBatches(users, 100)

批量插入也支持 Upsert和Create With Associations时,

CreateBatchSize选项初始化GORM,所有INSERT在创建记录和关联时都会遵守这个选项

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{CreateBatchSize: 1000,
})db := db.Session(&gorm.Session{CreateBatchSize: 1000})users = [5000]User{{Name: "jinzhu", Pets: []Pet{pet1, pet2, pet3}}...}db.Create(&users)
// INSERT INTO users xxx (5 batches)
// INSERT INTO pets xxx (15 batches)

1.4 创建钩子(Hooks)

GORM允许用户定义钩子实现BeforeSave, BeforeCreate, AfterSave, AfterCreate。这些钩子方法将在创建记录时被调用,有关生命周期的详细信息请参阅钩子

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {u.UUID = uuid.New()if u.Role == "admin" {return errors.New("invalid role")}return
}

如果你想跳过Hooks方法,你可以使用SkipHooks会话模式(Session mode),例如:

DB.Session(&gorm.Session{SkipHooks: true}).Create(&user)DB.Session(&gorm.Session{SkipHooks: true}).Create(&users)DB.Session(&gorm.Session{SkipHooks: true}).CreateInBatches(users, 100)

1.5 Create From Map

GORM支持从map[string]interface{}[]map[string]interface{}{}创建,例如:

db.Model(&User{}).Create(map[string]interface{}{"Name": "jinzhu", "Age": 18,
})// batch insert from `[]map[string]interface{}{}`
db.Model(&User{}).Create([]map[string]interface{}{{"Name": "jinzhu_1", "Age": 18},{"Name": "jinzhu_2", "Age": 20},
})

当从map创建时,钩子不会被调用,关联不会被保存,主键值也不会被返回填充

1.6 从 SQL Expression/Context Valuer 创建

GORM允许用SQL表达式插入数据,有两种方法来实现这个目标,从map[string]interface{}或自定义数据类型创建,例如:

// Create from map
db.Model(User{}).Create(map[string]interface{}{"Name": "jinzhu","Location": clause.Expr{SQL: "ST_PointFromText(?)", Vars: []interface{}{"POINT(100 100)"}},
})
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"));// Create from customized data type
type Location struct {X, Y int
}// Scan implements the sql.Scanner interface
func (loc *Location) Scan(v interface{}) error {// Scan a value into struct from database driver
}func (loc Location) GormDataType() string {return "geometry"
}func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {return clause.Expr{SQL:  "ST_PointFromText(?)",Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},}
}type User struct {Name     stringLocation Location
}db.Create(&User{Name:     "jinzhu",Location: Location{X: 100, Y: 100},
})
// INSERT INTO `users` (`name`,`location`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))

1.7 进阶

1.7.1 Create With Associations

在创建带有关联的数据时,如果其关联值不是零,则这些关联将被插入,并且将调用其Hooks方法。

type CreditCard struct {gorm.ModelNumber   stringUserID   uint
}type User struct {gorm.ModelName       stringCreditCard CreditCard
}db.Create(&User{Name: "jinzhu",CreditCard: CreditCard{Number: "411111111111"}
})
// INSERT INTO `users` ...
// INSERT INTO `credit_cards` ...

您可以使用SelectOmit 跳过保存关联,例如:

db.Omit("CreditCard").Create(&user)// skip all associations
db.Omit(clause.Associations).Create(&user)

1.7.2 默认值

你可以为使用default标签为字段定义默认值,例如:

type User struct {ID   int64Name string `gorm:"default:galeone"`Age  int64  `gorm:"default:18"`
}

然后,将零值字段插入数据库时将使用默认值

任何零值,如0"false将不会保存到数据库中那些字段定义的默认值,你可能想使用指针类型或Scanner/Valuer来避免这种情况,例如:

type User struct {gorm.ModelName stringAge  *int           `gorm:"default:18"`Active sql.NullBool `gorm:"default:true"`
}

你必须为数据库中有默认值或虚拟/生成值的字段设置默认标记,如果你想在迁移时跳过默认值定义,你可以使用default:(-),例如:

type User struct {ID        string `gorm:"default:uuid_generate_v3()"` // db funcFirstName stringLastName  stringAge       uint8FullName  string `gorm:"->;type:GENERATED ALWAYS AS (concat(firstname,' ',lastname));default:(-);"`
}

当使用虚拟/生成(virtual/generated)值时,你可能需要禁用它的创建/更新权限,检查字段级权限

1.8 Upsert / On Conflict

GORM为不同的数据库提供了兼容的Upsert支持

import "gorm.io/gorm/clause"// Do nothing on conflict
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&user)// Update columns to default value on `id` conflict
db.Clauses(clause.OnConflict{Columns:   []clause.Column{{Name: "id"}},DoUpdates: clause.Assignments(map[string]interface{}{"role": "user"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET ***; SQL Server
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE ***; MySQL// Use SQL expression
db.Clauses(clause.OnConflict{Columns:   []clause.Column{{Name: "id"}},DoUpdates: clause.Assignments(map[string]interface{}{"count": gorm.Expr("GREATEST(count, VALUES(count))")}),
}).Create(&users)
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `count`=GREATEST(count, VALUES(count));// Update columns to new value on `id` conflict
db.Clauses(clause.OnConflict{Columns:   []clause.Column{{Name: "id"}},DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age); MySQL// Update all columns to new value on conflict except primary keys and those columns having default values from sql func
db.Clauses(clause.OnConflict{UpdateAll: true,
}).Create(&users)
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age", ...;
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age`=VALUES(age), ...; MySQL

在高级查询中查看FirstOrInit, FirstOrCreate

更多细节请检出原始SQL和SQL Builder

2、Query

2.1 检索单个对象

GORM提供了FirstTakeLast方法来从数据库中检索单个对象,它在查询数据库时添加了LIMIT 1条件,如果没有找到记录,它将返回错误ErrRecordNotFound

// Get the first record ordered by primary key
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;// Get one record, no specified order
db.Take(&user)
// SELECT * FROM users LIMIT 1;// Get last record, ordered by primary key desc
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;result := db.First(&user)
result.RowsAffected // returns count of records found
result.Error        // returns error or nil// check error ErrRecordNotFound
errors.Is(result.Error, gorm.ErrRecordNotFound)

如果你想避免ErrRecordNotFound错误,你可以像db.Limit(1).Find(&user)一样使用Find, Find方法同时接受结构数据和切片数据

使用 Find而不限制单个对象db.Find(&user)将查询整个表并只返回第一个非性能和不确定性的对象

FirstLast方法将根据主键顺序分别查找第一个和最后一个记录。只有当指向目标结构的指针作为参数传递给方法时,或者当使用db.Model()指定模型时,它们才有效。此外,如果相关模型没有定义主键,则模型将按第一个字段排序。例如:

var user User
var users []User// works because destination struct is passed in
db.First(&user)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// works because model is specified using `db.Model()`
result := map[string]interface{}{}
db.Model(&User{}).First(&result)
// SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1// doesn't work
result := map[string]interface{}{}
db.Table("users").First(&result)// works with Take
result := map[string]interface{}{}
db.Table("users").Take(&result)// no primary key defined, results will be ordered by first field (i.e., `Code`)
type Language struct {Code stringName string
}
db.First(&Language{})
// SELECT * FROM `languages` ORDER BY `languages`.`code` LIMIT 1

用主键检索多个对象

如果主键是数字,则可以通过内联条件使用主键检索对象。当使用字符串时,需要特别注意避免SQL注入;详细信息请查看安全部分。

db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);

如果主键是一个字符串(例如,像uuid),查询将被写成如下:

db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";

当目标对象有一个主键时,主键将被用于构建条件,例如:

var user = User{ID: 10}
db.First(&user)
// SELECT * FROM users WHERE id = 10;var result User
db.Model(User{ID: 10}).First(&result)
// SELECT * FROM users WHERE id = 10;

2.2 检索所有对象

// Get all records
result := db.Find(&users)
// SELECT * FROM users;result.RowsAffected // returns found records count, equals `len(users)`
result.Error        // returns error

2.3 条件(Conditions)

2.3.1 String Conditions

// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;// Get all matched records
db.Where("name <> ?", "jinzhu").Find(&users)
// SELECT * FROM users WHERE name <> 'jinzhu';// IN
db.Where("name IN ?", []string{"jinzhu", "jinzhu 2"}).Find(&users)
// SELECT * FROM users WHERE name IN ('jinzhu','jinzhu 2');// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%jin%';// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

如果已经设置了对象的主键,则条件查询不会覆盖主键的值,而是将其用作’ and '条件。例如:

var user = User{ID: 10}
db.Where("id = ?", 20).First(&user)
// SELECT * FROM users WHERE id = 10 and id = 20 ORDER BY id ASC LIMIT 1

查询将给出record not found错误。因此,在你想使用变量(如user)从数据库获取新值之前,将主键属性(如id)设置为nil

2.3.2 Struct & Map Conditions

// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;// Slice of primary keys
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22

当使用struct查询时,GORM只会查询非零字段,这意味着如果你的字段值为0'',false或其他零值,它将不会被用来构建查询条件,例如:

db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu";

要在查询条件中包含零值,您可以使用map,它将包括所有键值作为查询条件,例如:

db.Where(map[string]interface{}{"Name": "jinzhu", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;

有关详细信息,请参见Specify Struct search fields。

2.3.3 指定Struct搜索字段

当使用struct进行搜索时,你可以在查询条件中通过指定使用struct中的特定值来将相关的字段名或dbname传递给Where(),例如:

db.Where(&User{Name: "jinzhu"}, "name", "Age").Find(&users)
// SELECT * FROM users WHERE name = "jinzhu" AND age = 0;db.Where(&User{Name: "jinzhu"}, "Age").Find(&users)
// SELECT * FROM users WHERE age = 0;

2.3.4 内联条件(Inline Condition)

查询条件可以以类似于Where的方式内联到FirstFind等方法中。

// Get by primary key if it were a non-integer type
db.First(&user, "id = ?", "string_primary_key")
// SELECT * FROM users WHERE id = 'string_primary_key';// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
// SELECT * FROM users WHERE name = "jinzhu";db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;// Struct
db.Find(&users, User{Age: 20})
// SELECT * FROM users WHERE age = 20;// Map
db.Find(&users, map[string]interface{}{"age": 20})
// SELECT * FROM users WHERE age = 20;

2.3.5 Not Conditions

构造Not 条件,类似于Where

db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;// Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;

2.3.6 Or Conditions

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2", Age: 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2", "age": 18}).Find(&users)
// SELECT * FROM users WHERE name = 'jinzhu' OR (name = 'jinzhu 2' AND age = 18);

对于更复杂的SQL查询。请参阅“高级查询”中的“分组条件”。

2.4 选择特定字段

Select允许您指定要从数据库检索的字段。否则,GORM将默认选择所有字段。

db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;

也可以智能选择字段

2.5 排序 (Order)

从数据库检索记录时指定顺序

db.Order("age desc, name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;// Multiple orders
db.Order("age desc").Order("name").Find(&users)
// SELECT * FROM users ORDER BY age desc, name;db.Clauses(clause.OrderBy{Expression: clause.Expr{SQL: "FIELD(id,?)", Vars: []interface{}{[]int{1, 2, 3}}, WithoutParentheses: true},
}).Find(&User{})
// SELECT * FROM users ORDER BY FIELD(id,1,2,3)

2.6 Limit & Offset

Limit指定要检索的最大记录数
Offset指定在开始返回记录之前要跳过的记录数

db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3;// Cancel limit condition with -1
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)db.Offset(3).Find(&users)
// SELECT * FROM users OFFSET 3;db.Limit(10).Offset(5).Find(&users)
// SELECT * FROM users OFFSET 5 LIMIT 10;// Cancel offset condition with -1
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
// SELECT * FROM users OFFSET 10; (users1)
// SELECT * FROM users; (users2)

有关如何制作分页器的详细信息,请参阅Pagination

2.7 Group By & Having

type result struct {Date  time.TimeTotal int
}db.Model(&User{}).Select("name, sum(age) as total").Where("name LIKE ?", "group%").Group("name").First(&result)
// SELECT name, sum(age) as total FROM `users` WHERE name LIKE "group%" GROUP BY `name` LIMIT 1db.Model(&User{}).Select("name, sum(age) as total").Group("name").Having("name = ?", "group").Find(&result)
// SELECT name, sum(age) as total FROM `users` GROUP BY `name` HAVING name = "group"rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
defer rows.Close()
for rows.Next() {...
}rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
defer rows.Close()
for rows.Next() {...
}type Result struct {Date  time.TimeTotal int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

2.8 Distinct

从模型中选择不同的值

db.Distinct("name", "age").Order("name, age desc").Find(&results)

Distinct works with Pluck and Count too

2.9 Joins

指定连接条件

type result struct {Name  stringEmail string
}db.Model(&User{}).Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&result{})
// SELECT users.name, emails.email FROM `users` left join emails on emails.user_id = users.idrows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {...
}db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)// multiple joins with parameter
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

2.9.1 Joins Preloading

你可以在单个SQL使用Joins快速加载关联,例如:

db.Joins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;// inner join
db.InnerJoins("Company").Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` INNER JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id`;

Join with conditions

db.Joins("Company", db.Where(&Company{Alive: true})).Find(&users)
// SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;

2.9.2 连接一个派生表

还可以使用Joins来连接派生表。

type User struct {Id  intAge int
}type Order struct {UserId     intFinishedAt *time.Time
}query := db.Table("order").Select("MAX(order.finished_at) as latest").Joins("left join user user on order.user_id = user.id").Where("user.age > ?", 18).Group("order.user_id")
db.Model(&Order{}).Joins("join (?) q on order.finished_at = q.latest", query).Scan(&results)
// SELECT `order`.`user_id`,`order`.`finished_at` FROM `order` join (SELECT MAX(order.finished_at) as latest FROM `order` left join user user on order.user_id = user.id WHERE user.age > 18 GROUP BY `order`.`user_id`) q on order.finished_at = q.latest

2.10 Scan

将结果扫描到结构体中的工作方式与使用Find的方式类似

type Result struct {Name stringAge  int
}var result Result
db.Table("users").Select("name", "age").Where("name = ?", "Antonio").Scan(&result)// Raw SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

3、高级查询

3.1 智能选择字段

GORM允许Select特定的字段,如果你经常在你的应用程序中使用它,也许你想为API定义一个更小的结构体,它可以自动选择特定的字段,例如:

type User struct {ID     uintName   stringAge    intGender string// hundreds of fields
}type APIUser struct {ID   uintName string
}// Select `id`, `name` automatically when querying
db.Model(&User{}).Limit(10).Find(&APIUser{})
// SELECT `id`, `name` FROM `users` LIMIT 10

QueryFields模式将为当前模型选择所有字段的名称

db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{QueryFields: true,
})db.Find(&user)
// SELECT `users`.`name`, `users`.`age`, ... FROM `users` // with this option// Session Mode
db.Session(&gorm.Session{QueryFields: true}).Find(&user)
// SELECT `users`.`name`, `users`.`age`, ... FROM `users`

3.2 锁定(FOR UPDATE)

GORM支持不同类型的锁,例如:

db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
// SELECT * FROM `users` FOR UPDATEdb.Clauses(clause.Locking{Strength: "SHARE",Table: clause.Table{Name: clause.CurrentTable},
}).Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`db.Clauses(clause.Locking{Strength: "UPDATE",Options: "NOWAIT",
}).Find(&users)
// SELECT * FROM `users` FOR UPDATE NOWAIT

更多细节请参考原始SQL和SQL Builder

3.3 子查询(SubQuery)

子查询可以嵌套在一个查询中,GORM可以在使用*gorm.DB对象作为参数时生成子查询

db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");subQuery := db.Select("AVG(age)").Where("name LIKE ?", "name%").Table("users")
db.Select("AVG(age) as avgage").Group("name").Having("AVG(age) > (?)", subQuery).Find(&results)
// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")

From SubQuery

GORM允许你用方法TableFROM子句中使用子查询,例如:

db.Table("(?) as u", db.Model(&User{}).Select("name", "age")).Where("age = ?", 18).Find(&User{})
// SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE `age` = 18subQuery1 := db.Model(&User{}).Select("name")
subQuery2 := db.Model(&Pet{}).Select("name")
db.Table("(?) as u, (?) as p", subQuery1, subQuery2).Find(&User{})
// SELECT * FROM (SELECT `name` FROM `users`) as u, (SELECT `name` FROM `pets`) as p

3.4 组条件 (Group Conditions)

使用组条件更容易编写复杂的SQL查询

db.Where(db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")),
).Or(db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"),
).Find(&Pizza{}).Statement// SELECT * FROM `pizzas` WHERE (pizza = "pepperoni" AND (size = "small" OR size = "medium")) OR (pizza = "hawaiian" AND size = "xlarge")

3.5 带有多列的 IN

选择带有多列的IN

db.Where("(name, age, role) IN ?", [][]interface{}{{"jinzhu", 18, "admin"}, {"jinzhu2", 19, "user"}}).Find(&users)
// SELECT * FROM users WHERE (name, age, role) IN (("jinzhu", 18, "admin"), ("jinzhu 2", 19, "user"));

3.6 命名参数

GORM 用sql.NamedArg或map[string]interface{}{}支持命名参数,例如:

db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user)
// SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu"db.Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu"}).First(&user)
// SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu" ORDER BY `users`.`id` LIMIT 1

查看Raw SQL和SQL Builder了解更多细节

3.7 Find To Map

GORM允许扫描结果到map[string]interface{}[]map[string]interface{},不要忘记指定ModelTable,例如:

result := map[string]interface{}{}
db.Model(&User{}).First(&result, "id = ?", 1)var results []map[string]interface{}
db.Table("users").Find(&results)

3.8 FirstOrInit

获取第一个匹配的记录或初始化一个具有给定条件的新实例(仅适用于structmap条件)

// User not found, initialize it with give conditions
db.FirstOrInit(&user, User{Name: "non_existing"})
// user -> User{Name: "non_existing"}// Found user with `name` = `jinzhu`
db.Where(User{Name: "jinzhu"}).FirstOrInit(&user)
// user -> User{ID: 111, Name: "Jinzhu", Age: 18}// Found user with `name` = `jinzhu`
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
// user -> User{ID: 111, Name: "Jinzhu", Age: 18}

如果没有找到记录,则初始化struct与更多属性,这些Attrs将不会用于构建SQL查询

// User not found, initialize it with give conditions and Attrs
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
// SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// user -> User{Name: "non_existing", Age: 20}// User not found, initialize it with give conditions and Attrs
db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user)
// SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// user -> User{Name: "non_existing", Age: 20}// Found user with `name` = `jinzhu`, attributes will be ignored
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 20}).FirstOrInit(&user)
// SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "Jinzhu", Age: 18}

Assign :不管struct是否被找到,给struct分配属性,这些属性不会被用来构建SQL查询,最终的数据也不会保存到数据库中

/ User not found, initialize it with give conditions and Assign attributes
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
// user -> User{Name: "non_existing", Age: 20}// Found user with `name` = `jinzhu`, update it with Assign attributes
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 20}).FirstOrInit(&user)
// SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "Jinzhu", Age: 20}

3.9 FirstOrCreate

获取第一个匹配的记录或在给定条件下创建一个新记录(只适用于struct, map条件),RowsAffected返回创建/更新记录的计数

// User not found, create a new record with give conditions
result := db.FirstOrCreate(&user, User{Name: "non_existing"})
// INSERT INTO "users" (name) VALUES ("non_existing");
// user -> User{ID: 112, Name: "non_existing"}
// result.RowsAffected // => 1// Found user with `name` = `jinzhu`
result := db.Where(User{Name: "jinzhu"}).FirstOrCreate(&user)
// user -> User{ID: 111, Name: "jinzhu", "Age": 18}
// result.RowsAffected // => 0

如果没有找到记录,创建具有更多属性的结构,这些Attrs将不会用于构建SQL查询

// User not found, create it with give conditions and Attrs
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
// user -> User{ID: 112, Name: "non_existing", Age: 20}// Found user with `name` = `jinzhu`, attributes will be ignored
db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "jinzhu", Age: 18}

Assign :无论是否找到记录,都为记录分配属性,并将它们保存回数据库。

// User not found, initialize it with give conditions and Assign attributes
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
// SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
// user -> User{ID: 112, Name: "non_existing", Age: 20}// Found user with `name` = `jinzhu`, update it with Assign attributes
db.Where(User{Name: "jinzhu"}).Assign(User{Age: 20}).FirstOrCreate(&user)
// SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
// UPDATE users SET age=20 WHERE id = 111;
// user -> User{ID: 111, Name: "jinzhu", Age: 20}

3.10 优化器/索引提示

优化器提示允许控制查询优化器选择某个查询执行计划,GORMgorm.io/hints支持它,如:

import "gorm.io/hints"db.Clauses(hints.New("MAX_EXECUTION_TIME(10000)")).Find(&User{})
// SELECT * /*+ MAX_EXECUTION_TIME(10000) */ FROM `users`

索引提示允许向数据库传递索引提示,以防查询计划器混淆。

import "gorm.io/hints"db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{})
// SELECT * FROM `users` USE INDEX (`idx_user_name`)db.Clauses(hints.ForceIndex("idx_user_name", "idx_user_id").ForJoin()).Find(&User{})
// SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`)"

参考优化提示/索引/评论了解更多细节

3.11 迭代

GORM支持遍历行

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Rows()
defer rows.Close()for rows.Next() {var user User// ScanRows is a method of `gorm.DB`, it can be used to scan a row into a structdb.ScanRows(rows, &user)// do something
}

3.12 FindInBatches

Query and process records in batch

// batch size 100
result := db.Where("processed = ?", false).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error {for _, result := range results {// batch processing found records}tx.Save(&results)tx.RowsAffected // number of records in this batchbatch // Batch 1, 2, 3// returns error will stop future batchesreturn nil
})result.Error // returned error
result.RowsAffected // processed records count in all batches

2.13 Query Hooks

GORM允许钩子AfterFind用于查询,它将在查询记录时被调用,详细信息请参阅hooks

func (u *User) AfterFind(tx *gorm.DB) (err error) {if u.Role == "" {u.Role = "user"}return
}

2.14 Pluck

从数据库中查询单列并扫描到一个切片,如果您想查询多个列,则使用Select scan 代替

var ages []int64
db.Model(&users).Pluck("age", &ages)var names []string
db.Model(&User{}).Pluck("name", &names)db.Table("deleted_users").Pluck("name", &names)// Distinct Pluck
db.Model(&User{}).Distinct().Pluck("Name", &names)
// SELECT DISTINCT `name` FROM `users`// Requesting more than one column, use `Scan` or `Find` like this:
db.Select("name", "age").Scan(&users)
db.Select("name", "age").Find(&users)

2.15 作用域 (Scopes)

作用域允许您指定可以作为方法调用引用的常用查询

func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {return db.Where("amount > ?", 1000)
}func PaidWithCreditCard(db *gorm.DB) *gorm.DB {return db.Where("pay_mode_sign = ?", "C")
}func PaidWithCod(db *gorm.DB) *gorm.DB {return db.Where("pay_mode_sign = ?", "C")
}func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {return func (db *gorm.DB) *gorm.DB {return db.Where("status IN (?)", status)}
}db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
// Find all credit card orders and amount greater than 1000db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
// Find all COD orders and amount greater than 1000db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
// Find all paid, shipped orders that amount greater than 1000

详细检查Scopes

2.16 Count

获取匹配记录计数

var count int64
db.Model(&User{}).Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Count(&count)
// SELECT count(1) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
// SELECT count(1) FROM users WHERE name = 'jinzhu'; (count)db.Table("deleted_users").Count(&count)
// SELECT count(1) FROM deleted_users;// Count with Distinct
db.Model(&User{}).Distinct("name").Count(&count)
// SELECT COUNT(DISTINCT(`name`)) FROM `users`db.Table("deleted_users").Select("count(distinct(name))").Count(&count)
// SELECT count(distinct(name)) FROM deleted_users// Count with Group
users := []User{{Name: "name1"},{Name: "name2"},{Name: "name3"},{Name: "name3"},
}db.Model(&User{}).Group("name").Count(&count)
count // => 3

相关文章:

GORM 基础 -- CRUD 接口

1、Create 1.1 创建纪录 user : User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}result : db.Create(&user) // pass pointer of data to Createuser.ID // 回填插入数据的主键 result.Error // 返回的 error 信息 result.RowsAffect…...

为什么0代码自动化测试越来越受欢迎?一文2000字解析

目录 01、什么是零代码自动化测试 02、为什么零代码自动化测试越来越受欢迎 03、有代码和零代码自动化有什么区别 04、零代码自动化测试可以帮助你做什么 05、零代码自动化测试方法&#xff1a;NLP&#xff08;自然语言处理&#xff09; 06、为什么我们需要零代码自动化测…...

cleanmymac最新2023版 mac清理软件CleanMyMac X4.12.5 中文版功能介绍

CleanMyMac X4.12.5 中文版只需两个简单步骤就可以把系统里那些乱七八糟的无用文件统统清理掉&#xff0c;节省宝贵的磁盘空间。cleanmymac x个人认为X代表界面上的最大升级&#xff0c;功能方面有更多增加&#xff0c;与最新macOS系统更加兼容&#xff0c;流畅地与系统性能更加…...

pyhon部署注意事项

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…...

宣城x移动云,打造“城市级物联感知平台”

随着新一代信息技术与城市现代化的深度融合&#xff0c;智慧城市建设的重要性也愈发凸显。而在智慧城市建设中&#xff0c;物联网感知体系扮演着中枢神经系统的角色。 安徽宣城紧抓长三角城市群一体化发展机遇&#xff0c;为构建“数字宣城”建设发展新模式&#xff0c;携手移…...

英伟达Jetson NX套件刷机,配置Ubuntu20。

0. 前言 人并没有眼见得那么光鲜亮丽&#xff0c;博客也是。 今天推荐一本书《一百个人的十年》&#xff0c;没错就是我们的那十年&#xff08;60年代&#xff09;。写得很真实&#xff0c;牛棚猪圈&#xff0c;确实如此。 1. SdkManager安装 官网下载。 打开终端 执行命令sud…...

Vue计算属性

计算属性 ​ 计算属性的重点突出在属性两个字上(属性是名词)&#xff0c;首先它是个属性其次这个属性有计算的能力(计算是动词)&#xff0c;这里的计算就是个函数;简单点说&#xff0c;它就是一个能够将计算结果缓存起来的属性(将行为转化成了静态的属性)&#xff0c;仅此而已…...

代码随想录刷题-字符串-反转字符串

文章目录反转字符串习题双指针swap 的两种方式反转字符串 本节对应代码随想录中&#xff1a;代码随想录&#xff0c;讲解视频&#xff1a;字符串基础操作&#xff01; | LeetCode&#xff1a;344.反转字符串_哔哩哔哩_bilibili 习题 题目链接&#xff1a;344. 反转字符串 - …...

14-链表练习-剑指 Offer II 021. 删除链表的倒数第 n 个结点

题目 给定一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5] 示例 2&#xff1a; 输入&#xff1a;head [1], n 1 输出&#xff1a;[] 示例 3&…...

用Java解决华为OD机试考题,真的高效,真的强,来吧,清单奉上,祝你上岸

华为 OD 机试题最新&#xff08;Java&#xff09;清单&#xff08;机试题库还在逐日更新&#xff09; 题库目录 直接在本页使用 CtrlF&#xff0c;输入题目名称就可以进行检索。 序号文章分值1【华为OD机试真题JAVA】快递装载问题_国服第二切图仔的博客-CSDN博客1002【华为…...

【Stable Diffusion】Stable Diffusion免安装在线部署教程

一、开启Google Colab网址 官网&#xff1a;https://colab.research.google.com/ 点击添加代码&#xff1a; 二、执行如下代码指令 !pip install --upgrade fastapi0.90.1 !git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui !git clone https://github.…...

Jetson设备如何接调试串口工具查看内核打印信息

方便小白使用如下教程。 一、认识USB转串口调试工具转接小板 和硬件连接方式 如图&#xff0c;是一款USB TO TTL转换板&#xff0c;这款小板支持3种供电模式&#xff1a;对外输出5V、对外输出3.3V和由外部供电。正面有一个跳帽&#xff0c;跳帽跳到3V3&#xff0c;小板由US…...

一直被低估的美图,正悄悄成为AIGC领跑者

【潮汐商业评论/原创】 也许多年之后再回望历史&#xff0c;2023年将被视为标志性的一年。它不仅是疫情之后的复苏之年&#xff0c;更是人工智能在中国乃至全球迎来爆发的一年。 从来没有这样的景象——在2023年的前3个月&#xff0c;全球互联网被AIGC话题“刷屏”&#xff0…...

JAVA开发与运维(JavaWeb测试环境搭建)

本例子测试环境搭建在腾讯云平台之上。 系统架构&#xff1a; 微服务EurekaApollogateWayredisrocketMqOSSsparkETLmysqlpgsqlclickHouseSLB. 首先需要申请的云资源。 业务用途CPUMEMDisk数量云产品规格服务器应用服务&#xff08;部署微服务&#xff09;4核8G500G1CVMS6.L…...

python 的range函数你需要知道三件事

python 的range函数你需要知道三件事python 的range() 函数你需要知道三件事一、range函数的功能和语法二、range函数转化为数组三、range函数与for语句的应用python 的range() 函数你需要知道三件事 一、range函数的功能和语法 **1、range函数的功能&#xff1a;**range&…...

穿越周期的进击,科沃斯“敢”于变革

文|智能相对论 作者|佘凯文 什么样的扫地机器人才是一款好的扫地机器人&#xff1f; 回答这个问题我们首先要明白扫地机器人的产品逻辑究竟是什么。简单来说&#xff0c;就是替代人们完成一定环境内的清洁工作&#xff0c;它能完成的“清洁程度”越深则代表其产品力越强。 …...

不使用IF语句对一组数进行排序的分析和实现

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、不使用IF语句的两数排序方法二、不使用IF的多数排序讨论1、三数比较和排序2、多个数据比较和排序总结前言 这个题目源于已经完成了不使用IF语句对两个数的比…...

在大厂做了5年测试,3月被无情辞退,想给摸鱼的兄弟提个醒

先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入字节&#xff0c;以“人员优化”的名义无情被裁员&#xff0c;之后跳槽到了有赞&#xff0c;一直从事软件测试的工作。之前没有实习经历&#xff0c;算是5年的工作经验吧。 这5年之间完成了一次…...

【职业规划】第二篇:程序员分级之中级程序员

Java程序员的分级并没有统一的标准,以下列举出来的只是我所理解的关于Java工程师的划分标准,不喜勿喷,如有建议,欢迎评论或私信。 二、Java中级程序员(又名:Java中级工程师/Java中级开发) 1.级别介绍与职责 简单一句话总结中级程序员就是:知道是什么。 具体些就是,…...

Studio One没有声音怎么办 Studio One工程没有声音

Studio One是一款非常优秀编曲软件&#xff0c;能够帮助用户高效的进行编曲和创作&#xff0c;也是目前主流的通道机架软件之一&#xff0c;受到很多音乐编曲爱好者的追捧。但是很多刚接触这款软件的小伙伴会碰到这样或者那样的问题&#xff0c;比如Stuidio one没有声音怎么办&…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...

.Net框架,除了EF还有很多很多......

文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

工程地质软件市场:发展现状、趋势与策略建议

一、引言 在工程建设领域&#xff0c;准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具&#xff0c;正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

拉力测试cuda pytorch 把 4070显卡拉满

import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试&#xff0c;通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小&#xff0c;增大可提高计算复杂度duration: 测试持续时间&#xff08;秒&…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...