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

CRUD

Gorm教程 - CRUD

创建

创建记录

user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}result := db.Create(&user) // 通过数据的指针来创建user.ID             // 返回插入数据的主键
result.Error        // 返回 error
result.RowsAffected // 返回插入记录的条数

我们还可以使用 Create() 创建多项记录:

users := []*User{{Name: "Jinzhu", Age: 18, Birthday: time.Now()},{Name: "Jackson", Age: 19, Birthday: time.Now()},
}result := db.Create(users) // pass a slice to insert multiple rowresult.Error        // returns error
result.RowsAffected // returns inserted records count

用指定的字段创建记录

创建记录并为指定字段赋值。

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

创建记录并忽略传递给 ‘Omit’ 的字段值

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

批量插入

要高效地插入大量记录,请将切片传递给Create方法。 GORM 将生成一条 SQL 来插入所有数据,以返回所有主键值,并触发 Hook 方法。 当这些记录可以被分割成多个批次时,GORM会开启一个事务来处理它们。

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

你可以通过db.CreateInBatches方法来指定批量插入的批次大小

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

创建钩子

GORM允许用户通过实现这些接口 BeforeSave, BeforeCreate, AfterSave, AfterCreate来自定义钩子。 这些钩子方法会在创建一条记录时被调用,关于钩子的生命周期请参阅Hooks。

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会话模式,例子如下

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)

创建时可用的 hook

// 开始事务
BeforeSave
BeforeCreate
// 关联前的 save
// 插入记录至 db
// 关联后的 save
AfterCreate
AfterSave
// 提交或回滚事务

高级选项

关联创建

创建关联数据时,如果关联值非零,这些关联会被upsert,并且它们的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` ...

你可以通过Select, Omit方法来跳过关联更新,示例如下:

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

Upsert 及冲突

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`=V

查询

检索单个对象

GORM 提供了 FirstTakeLast 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;result := db.First(&user)
result.RowsAffected // 返回找到的记录数
result.Error        // returns error or nil// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

根据主键检索

如果主键是数字类型,您可以使用 内联条件 来检索对象。 当使用字符串时,需要额外的注意来避免SQL注入;查看 Security 部分来了解详情。

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;

检索全部对象

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

条件

String 条件

// 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。

Struct & Map 条件

// 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);

NOTE When querying with struct, GORM will only query with non-zero fields, that means if your field’s value is 0, '', false or other zero values, it won’t be used to build query conditions, for example:

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

To include zero values in the query conditions, you can use a map, which will include all key-values as query conditions, for example:

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

指定结构体查询字段

When searching with struct, you can specify which particular values from the struct to use in the query conditions by passing in the relevant field name or the dbname to Where(), for example:

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;

内联条件

Query conditions can be inlined into methods like First and Find in a similar way to Where.

// 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;

Not 条件

Build NOT conditions, works similar to 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;

Or 条件

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

For more complicated SQL queries. please also refer to Group Conditions in Advanced Query.

选择特定字段

Select allows you to specify the fields that you want to retrieve from database. Otherwise, GORM will select all fields by default.

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;

Also check out Smart Select Fields

排序

Specify order when retrieving records from the database

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)

Limit & Offset

Limit specify the max number of records to retrieve Offset specify the number of records to skip before starting to return the records

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)

Refer to Pagination for details on how to make a paginator

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)

Distinct

Selecting distinct values from the model

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

Distinct works with Pluck and Count too

Joins

Specify Joins conditions

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)

Joins 预加载

You can use Joins eager loading associations with a single SQL, for example:

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;

For more details, please refer to Preloading (Eager Loading).

Joins 一个衍生表

You can also use Joins to join a derived table.

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

Scan

Scanning results into a struct works similarly to the way we use 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)

更新

保存所有字段

Save 会保存所有的字段,即使字段是零值

db.First(&user)user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;

保存 是一个组合函数。 如果保存值不包含主键,它将执行 Create,否则它将执行 Update (包含所有字段)。

更新单个列

当使用 Update 更新单列时,需要有一些条件,否则将会引起ErrMissingWhereClause 错误,查看 阻止全局更新 了解详情。 当使用 Model 方法,并且它有主键值时,主键将会被用于构建条件,例如:

// 根据条件更新
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;// User 的 ID 是 `111`
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;// 根据条件和 model 的值进行更新
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;

更新多列

Updates 方法支持 structmap[string]interface{} 参数。当使用 struct 更新时,默认情况下GORM 只会更新非零值的字段

// 根据 `struct` 更新属性,只会更新非零值的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;

更新选定字段

如果您想要在更新时选择、忽略某些字段,您可以使用 SelectOmit

// 选择 Map 的字段
// User 的 ID 是 `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;// 选择 Struct 的字段(会选中零值的字段)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;// 选择所有字段(选择包括零值字段的所有字段)
db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})// 选择除 Role 外的所有字段(包括零值字段的所有字段)
db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})

更新 Hook

GORM 支持的 hook 包括:BeforeSave, BeforeUpdate, AfterSave, AfterUpdate. 更新记录时将调用这些方法,查看 Hooks 获取详细信息

func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {if u.Role == "admin" {return errors.New("admin user not allowed to update")}return
}

更新时可用的 hook

// 开始事务
BeforeSave
BeforeUpdate
// 关联前的 save
// 更新 db
// 关联后的 save
AfterUpdate
AfterSave
// 提交或回滚事务

批量更新

如果没有通过 Model 指定一个含有主键的记录,GORM 会执行批量更新

// Update with struct
db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin';// Update with map
db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);

阻止全局更新

如果你执行一个没有任何条件的批量更新,GORM 默认不会运行,并且会返回 ErrMissingWhereClause 错误

你可以用一些条件,使用原生 SQL 或者启用 AllowGlobalUpdate 模式,例如:

db.Model(&User{}).Update("name", "jinzhu").Error // gorm.ErrMissingWhereClausedb.Model(&User{}).Where("1 = 1").Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu" WHERE 1=1db.Exec("UPDATE users SET name = ?", "jinzhu")
// UPDATE users SET name = "jinzhu"db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "jinzhu")
// UPDATE users SET `name` = "jinzhu"

使用 SQL 表达式更新

GORM 允许用 SQL 表达式更新列,例如:

// product's ID is `3`
db.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;db.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})
// UPDATE "products" SET "price" = price * 2 + 100, "updated_at" = '2013-11-17 21:34:10' WHERE "id" = 3;db.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3;db.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = 3 AND quantity > 1;

根据子查询进行更新

使用子查询更新一个表

db.Model(&user).Update("company_name", db.Model(&Company{}).Select("name").Where("companies.id = users.company_id"))
// UPDATE "users" SET "company_name" = (SELECT name FROM companies WHERE companies.id = users.company_id);db.Table("users as u").Where("name = ?", "jinzhu").Update("company_name", db.Table("companies as c").Select("name").Where("c.id = u.company_id"))db.Table("users as u").Where("name = ?", "jinzhu").Updates(map[string]interface{}{"company_name": db.Table("companies as c").Select("name").Where("c.id = u.company_id")})

不使用 Hook 和时间追踪

如果你希望更新时跳过 Hook 方法,并且不追踪更新的时间,你可以使用 UpdateColumn, UpdateColumns, 它们的用法类似于 Update, Updates

// Update single column
db.Model(&user).UpdateColumn("name", "hello")
// UPDATE users SET name='hello' WHERE id = 111;// Update multiple columns
db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE id = 111;// Update selected columns
db.Model(&user).Select("name", "age").UpdateColumns(User{Name: "hello", Age: 0})
// UPDATE users SET name='hello', age=0 WHERE id = 111;

删除

删除一条记录

删除一条记录时,删除对象需要指定主键,否则会触发 批量删除,例如:

// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";

根据主键删除

GORM 允许通过主键(可以是复合主键)和内联条件来删除对象,它可以使用数字(如以下例子。也可以使用字符串——译者注)。查看 查询-内联条件(Query Inline Conditions) 了解详情。

db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);

钩子函数

对于删除操作,GORM 支持 BeforeDeleteAfterDelete Hook,在删除记录时会调用这些方法,查看 Hook 获取详情

func (u *User) BeforeDelete(tx *gorm.DB) (err error) {if u.Role == "admin" {return errors.New("admin user not allowed to delete")}return
}

删除时可用的 hook

// 开始事务
BeforeDelete
// 删除 db 中的数据
AfterDelete
// 提交或回滚事务

批量删除

如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录

db.Where("email LIKE ?", "%jinzhu%").Delete(&Email{})
// DELETE from emails where email LIKE "%jinzhu%";db.Delete(&Email{}, "email LIKE ?", "%jinzhu%")
// DELETE from emails where email LIKE "%jinzhu%";

可以将一个主键切片传递给Delete 方法,以便更高效的删除数据量大的记录

var users = []User{{ID: 1}, {ID: 2}, {ID: 3}}
db.Delete(&users)
// DELETE FROM users WHERE id IN (1,2,3);db.Delete(&users, "name LIKE ?", "%jinzhu%")
// DELETE FROM users WHERE name LIKE "%jinzhu%" AND id IN (1,2,3); 

阻止全局删除

当你试图执行不带任何条件的批量删除时,GORM将不会运行并返回ErrMissingWhereClause 错误

如果一定要这么做,你必须添加一些条件,或者使用原生SQL,或者开启AllowGlobalUpdate 模式,如下例:

db.Delete(&User{}).Error // gorm.ErrMissingWhereClausedb.Delete(&[]User{{Name: "jinzhu1"}, {Name: "jinzhu2"}}).Error // gorm.ErrMissingWhereClausedb.Where("1 = 1").Delete(&User{})
// DELETE FROM `users` WHERE 1=1db.Exec("DELETE FROM users")
// DELETE FROM usersdb.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
// DELETE FROM users

软删除

如果你的模型包含了 gorm.DeletedAt字段(该字段也被包含在gorm.Model中),那么该模型将会自动获得软删除的能力!

当调用Delete时,GORM并不会从数据库中删除该记录,而是将该记录的DeleteAt设置为当前时间,而后的一般查询方法将无法查找到此条记录。

查找被软删除的记录

你可以使用Unscoped来查询到被软删除的记录

db.Unscoped().Where("age = 20").Find(&users)
// SELECT * FROM users WHERE age = 20;

永久删除

你可以使用 Unscoped来永久删除匹配的记录

db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;

SQL 构建器

原生 SQL

原生查询 SQL 和 Scan

type Result struct {ID   intName stringAge  int
}var result Result
db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)db.Raw("SELECT id, name, age FROM users WHERE name = ?", "jinzhu").Scan(&result)var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)

Exec 原生 SQL

db.Exec("DROP TABLE users")
db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})// Exec with SQL Expression
db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")

DryRun 模式

在不执行的情况下生成 SQL 及其参数,可以用于准备或测试生成的 SQL,详情请参考 Session

stmt := db.Session(&gorm.Session{DryRun: true}).First(&user, 1).Statement
stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 ORDER BY `id`
stmt.Vars         //=> []interface{}{1}

Row & Rows

获取 *sql.Row 结果

// 使用 GORM API 构建 SQL
row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age)// 使用原生 SQL
row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email)

获取 *sql.Rows 结果

// 使用 GORM API 构建 SQL
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next() {rows.Scan(&name, &age, &email)// 业务逻辑...
}// 原生 SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
defer rows.Close()
for rows.Next() {rows.Scan(&name, &age, &email)// 业务逻辑...
}

将 sql.Rows 扫描至 model

使用 ScanRows 将一行记录扫描至 struct,例如:

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()var user User
for rows.Next() {// ScanRows 将一行扫描至 userdb.ScanRows(rows, &user)// 业务逻辑...
}
http://www.sczhlp.com/news/554.html

相关文章:

  • 暑期周总结(五)
  • 用 Python 实现多干扰线图像验证码的识别系统
  • Python 实现多干扰线图像验证码识别
  • 学习链接
  • helm环境快速部署实战
  • PlantUML绘制时序图
  • Datawhale AI夏令营 Dify入门 Task05 智能客服
  • ICPC 2024 网络赛(I)
  • LED控制原理
  • 【ESP8266】Vscode + platformIo + Esp8266 新建工程 关键步骤
  • Revo Uninstaller Pro专业版领取:2025最佳Windows软件卸载工具
  • 北大 2024 强基数学
  • 付老师名言
  • [羊城杯 2021]Baby_Forenisc-内存取证-Volatility 2工具下载使用- Volatility 2.6 的 Linux 免安装版(Standalone 版本)
  • 开发集合控件的拖拽流程优化——以TreeView为例
  • 第七天
  • 基于深度学习的YOLO框架的7种交通场景识别项目系统【附完整源码+数据集】
  • 2-2 点灯例程(寄存器开发) - LI,Yi
  • 【Datawhale AI夏令营--task2】科大讯飞AI大赛(大模型技术)
  • 记录一次vue3+mqtt.js连接华为云mqtt的成功经历
  • 狂神说Java|Java基础
  • 每日题单
  • 在常量时间内实现单向链表的插入与删除
  • cpp的单头文件
  • (阶段三:整合)面向用户 面向商户,场景之:shop
  • 现代Web框架的性能基准测试(6084)
  • 服务端推送技术的现代实现(8430)
  • 跨平台Web服务开发的新选择(1992)
  • Astro机器人流畅运动背后的科技原理
  • 实时通信协议的Rust实现(5234)