上一篇:GROM GEN的代码生成
下一篇:GORM GEN的最佳实践
推荐有时间的同学可以将系列文章都过一遍有个印象、没有时间的同学可以通过关键词搜索找到自己需要的内容。
前面详细介绍代码生成的能力,包括直接同步数据库表生成、基于已有的model生成以及通过建表SQL生成三部分。本文将介绍如使用GEN生成的代码执行CRUD操作。
创建/插入
示例代码:github.com/go-gorm/gen…
1.1 初始化
这里可以看下前一篇代码生成,GEN推荐大家配置WithDefaultQuery模式,这样会生成全局单例变量,初始化一次其他地方可以直接使用。初始化建议调用一次,通常是直接写在init里面。
DefaultQuery模式生成的代码示例:
var (
Q = new(Query)
Role *role
User *user
)
func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
*Q = *Use(db, opts...)
Role = &Q.Role
User = &Q.User
}
新建一个init.go,调用初始化逻辑SetDefault
func init() {
mysql.Init()
SetDefault(mysql.DB(context.Background()))
}
1.2【批量】创建
主要包括创建/选择或者忽略字段的创建/批量的创建用法
package handler
import (
"context"
"github.com/go-gorm/gendemo/biz/dal/model"
"github.com/go-gorm/gendemo/biz/dal/query"
"github.com/go-gorm/gendemo/logs"
"gorm.io/gorm/clause"
)
// SimpleCreate 简单的创建
func SimpleCreate(ctx context.Context) {
vd := query.User
user := &model.User{Name: "1", Phone: "12xxx"}
//简单创建
err := vd.WithContext(ctx).Create(user)
if err != nil {
logs.CtxError(ctx, "[SimpleCreate] Create err=%s", err.Error())
return
}
}
// SelectCreate 选择字段创建
func SelectCreate(ctx context.Context) {
vd := query.User
user := &model.User{Name: "1", Phone: "12xxx"}
//创建语句只会写shop_id 字段
err := vd.WithContext(ctx).Select(vd.Name).Create(user)
if err != nil {
logs.CtxError(ctx, "[SelectCreate] Create err=%s", err.Error())
return
}
}
// IgnoreCreate 忽略字段创建
func IgnoreCreate(ctx context.Context) {
vd := query.User
user := &model.User{Name: "1", Phone: "12xxx"}
//创建语句忽略shop_id 字段
err := vd.WithContext(ctx).Omit(vd.Name).Create(user)
if err != nil {
logs.CtxError(ctx, "[IgnoreCreate] Create err=%s", err.Error())
return
}
}
// BatchCreate 批量创建
func BatchCreate(ctx context.Context) {
vd := query.User
users := []*model.User{
{Name: "1", Phone: "11xxx"},
{Name: "2", Phone: "12xxx"},
{Name: "3", Phone: "13xxx"},
}
//批量创建
err := vd.WithContext(ctx).Create(users...)
if err != nil {
logs.CtxError(ctx, "[BatchCreate] Create err=%s", err.Error())
return
}
//批量创建,设置批量创建数量2,即一次创建两条
err = vd.WithContext(ctx).CreateInBatches(users, 2)
if err != nil {
logs.CtxError(ctx, "[BatchCreate] Create err=%s", err.Error())
return
}
}
1.3 创建更新(upsert)
主要介绍如何处理,创建冲突的情况(主键和唯一键冲突);包括全量更新/部分更新以及忽略不更新
// Upsert 更新创建
func Upsert(ctx context.Context) {
vd := query.User
users := []*model.User{
{Name: "1", Phone: "11xxx"},
{Name: "2", Phone: "12xxx"},
{Name: "3", Phone: "13xxx"},
}
//创建时冲突直接忽略
err := vd.WithContext(ctx).Clauses(clause.OnConflict{DoNothing: true}).Create(users...)
if err != nil {
logs.CtxError(ctx, "[Upsert] Create err=%s", err.Error())
return
}
//创建时冲突,更新所有字段
err = vd.WithContext(ctx).Clauses(clause.OnConflict{UpdateAll: true}).Create(users...)
if err != nil {
logs.CtxError(ctx, "[Upsert] Create err=%s", err.Error())
return
}
//创建时冲突,更新指定字段
err = vd.WithContext(ctx).Clauses(clause.OnConflict{
DoUpdates: clause.Assignments(map[string]interface{}{"shop_id": "group_id"}),
}).Create(users...)
if err != nil {
logs.CtxError(ctx, "[Upsert] Create err=%s", err.Error())
return
}
}
更新
示例代码:github.com/go-gorm/gen…
2.1 简单更新
这部分主要介绍,如何更新单个字段,如何选择要更新的字段,以及如何更新struct的零值字段等
package handler
import (
"context"
"github.com/go-gorm/gendemo/biz/dal/model"
"github.com/go-gorm/gendemo/biz/dal/query"
"github.com/go-gorm/gendemo/logs"
)
// SimpleUpdate 简单的更新
func SimpleUpdate(ctx context.Context) {
vd := query.User
//只更新单个字段
update, err := vd.WithContext(ctx).Where(vd.ID.Eq(1)).Update(vd.Name, "1")
if err != nil {
logs.CtxError(ctx, "[SimpleUpdate] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[SimpleUpdate] Update RowsAffected=0")
}
//更新有限多字段
update, err = vd.WithContext(ctx).Where(vd.ID.Eq(1)).
UpdateSimple(vd.Name.Value("2"), vd.Extra.Zero())
if err != nil {
logs.CtxError(ctx, "[SimpleUpdate] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[SimpleUpdate] Update RowsAffected=0")
}
//通过struct更新,默认是会忽略零值,也就是这里只会更新 shop_id和group_id
user := &model.User{Name: "1", Phone: "12xxx"}
update, err = vd.WithContext(ctx).Where(vd.ID.Eq(1)).
Updates(user)
if err != nil {
logs.CtxError(ctx, "[SimpleUpdate] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[SimpleUpdate] Update RowsAffected=0")
}
//通过struct更新,还想更新非零值,可以加sleet
update, err = vd.WithContext(ctx).Where(vd.ID.Eq(1)).
Select(vd.Name,vd.Extra).Updates(user)
//或者直接更新所有,select *
update, err = vd.WithContext(ctx).Where(vd.ID.Eq(1)).
Select(vd.ALL).Updates(user)
if err != nil {
logs.CtxError(ctx, "[SimpleUpdate] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[SimpleUpdate] Update RowsAffected=0")
}
//当然除了上面的方式,也可以用map,会更新map里的所有字段
update, err = vd.WithContext(ctx).Where(vd.ID.Eq(1)).
Updates(map[string]interface{}{"name": "3", "phone": 3})
if err != nil {
logs.CtxError(ctx, "[SimpleUpdate] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[SimpleUpdate] Update RowsAffected=0")
}
}
2.2 基于查询更新
主要适用于,本次更新的字段的值 来源于另一个查询的结果
// UpdateFromQuery 查询更新
//!!!这里只是举例如何根据查询更新,没有任何业务意义甚至看起来更新很不合理
func UpdateFromQuery(ctx context.Context) {
vd := query.User
co := query.Role
update, err := vd.WithContext(ctx).Debug().Where(vd.ID.Eq(1)).
Update(vd.Name, co.WithContext(ctx).Select(co.Name).Where(co.ID.Eq(3)))
//UPDATE `user` SET `name`=(SELECT `role`.`name` FROM `role` WHERE `role`.`id` = 3)
//WHERE `user`.`id` = 1
if err != nil {
logs.CtxError(ctx, "[UpdateFromQuery] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[UpdateFromQuery] Update RowsAffected=0")
}
//多个字段更新
vdt := vd.As("vdt")
ua := vd.As("ua")
update, err = ua.WithContext(ctx).Debug().
UpdateFrom(vdt.WithContext(ctx).Select(vd.Name, vd.Phone).Where(vd.ID.Eq(1))).
Where(ua.ID.Eq(2)).UpdateSimple(
ua.Name.SetCol(vdt.Name), ua.Phone.SetCol(vdt.Phone))
//UPDATE `user` AS `ua`,
//(SELECT `user`.`name`,`user`.`phone` FROM `user` WHERE `user`.`id` = 1) AS `vdt`
//SET `ua`.`name`=`vdt`.`name`,`ua`.`phone`=`vdt`.`phone` WHERE `ua`.`id` = 2
if err != nil {
logs.CtxError(ctx, "[UpdateFromQuery] Update err=%s", err.Error())
return
}
if update.RowsAffected <= 0 {
logs.CtxWarn(ctx, "[UpdateFromQuery] Update RowsAffected=0")
}
}
删除
示例代码:github.com/go-gorm/gen…
删除这里没有太多的逻辑,一般就是带上条件直接执行删除就行;可能唯一想要关注的就是软删除。
package handler
import (
"context"
"github.com/go-gorm/gendemo/biz/dal/query"
"github.com/go-gorm/gendemo/logs"
)
// SimpleDelete 简单的删除
func SimpleDelete(ctx context.Context) {
vd := query.User
//指定条件
result, err := vd.WithContext(ctx).Where(vd.Name.Eq("1")).Delete()
if err != nil {
logs.CtxError(ctx, "[SimpleDelete] Delete err=%s", err.Error())
return
}
if result.RowsAffected == 0 {
//no deleted record
}
//如果你的结构里有软删除字段,默认逻辑都是走软删除
//具体的可以参考 https://gorm.io/docs/delete.html#Find-soft-deleted-records
//软删除的情况下,也是可以实现物理删除的,加上 Unscoped()
result, err = vd.WithContext(ctx).Where(vd.Name.Eq("1")).Unscoped().Delete()
if err != nil {
logs.CtxError(ctx, "[SimpleDelete] Delete err=%s", err.Error())
return
}
}
事务
示例代码:
github.com/go-gorm/gen…
事务推荐使用Transaction方法自动管理事务的开始和提交/回滚;当然也支持手动管理事务,切记手动开事务之后一定要判断err;同时开完事务一定要记得提交或者回滚。
package handler
import (
"context"
"github.com/go-gorm/gendemo/biz/dal/model"
"github.com/go-gorm/gendemo/biz/dal/query"
"github.com/go-gorm/gendemo/logs"
)
// AutoTransAction 自动事务
func AutoTransAction(ctx context.Context) {
//通过Transaction,开事务,只要返回err就会自动回滚,没有err就会自动提交
err := query.Q.Transaction(func(tx *query.Query) error {
//事务里要用事务的query操作(tx)
err := tx.Role.WithContext(ctx).Create(&model.Role{Name: "test"})
if err != nil { //有err返回
return err
}
err = tx.User.WithContext(ctx).Create(&model.User{Name: "1"})
if err != nil { //有err返回自动回滚
return err
}
return err
})
if err != nil {
logs.CtxError(ctx, "[AutoTransAction] Transaction err=%s", err.Error())
}
//事务也支持嵌套
err = query.Q.Transaction(func(tx *query.Query) error {
//事务里要用事务的query操作(tx)
cerr := tx.Role.WithContext(ctx).Create(&model.Role{Name: "test"})
if cerr != nil { //有err返回
return cerr
}
tx.Transaction(func(tx2 *query.Query) error {
uerr := tx2.User.WithContext(ctx).Create(&model.User{Name: "1"})
if uerr != nil { //有err返回自动回滚
return uerr
}
return tx2.User.WithContext(ctx).Create(&model.User{Name: "2"})
})
return err
})
if err != nil {
logs.CtxError(ctx, "[AutoTransAction] Transaction err=%s", err.Error())
}
}
// ManualTransAction 手动事务
func ManualTransAction(ctx context.Context) {
//开启事务
tx := query.Q.Begin()
//记得判断err
if tx.Error != nil {
//事务开失败了
return
}
var err error
defer func() {
if recover() != nil || err != nil {
//回滚事务
_ = tx.Rollback()
}
}()
err = tx.Role.WithContext(ctx).Create(&model.Role{Name: "test"})
if err != nil {
return
}
//提交事务
err = tx.Commit()
}
查询
示例:
github.com/go-gorm/gen…
5.1 字段类型和操作
查询部分主要是构建查询条件,所以这里先列举下每种dao字段类型拥有的操作。
id := field.NewField("user", "id")
anotherID := field.NewField("another", "id")
// `user`.`id` = `another`.`id`
id.EqCol(anotherID)
// user`.`id` IS NULL
id.IsNull()
// int field
f := field.NewInt("user", "id")
// `user`.`id` = 123
f.Eq(123)
// `user`.`id` DESC
f.Desc()
// `user`.`id` AS `user_id`
f.As("user_id")
// COUNT(`user`.`id`)
f.Count()
// SUM(`user`.`id`)
f.Sum()
// SUM(`user`.`id`) > 123
f.Sum().Gt(123)
// ((`user`.`id`+1)*2)/3
f.Add(1).Mul(2).Div(3),
// `user`.`id` <<< 3
f.LeftShift(3)
浮点型(Float):Eq/Neq/Gt/Gte/Lt/Lte/In/NotIn/Between/NotBetween/Like/NotLike/Add/Sub/Mul/Div/FloorDiv/Floor/Value/Zero/Sum/IfNull
字符串(String):Eq/Neq/Gt/Gte/Lt/Lte/Between/NotBetween/In/NotIn/Like/NotLike/Regexp/NotRegxp/FindInSet/FindInSetWith/Value/Zero/IfNull
name := field.NewString("user", "name")
// `user`.`name` = "modi"
name.Eq("modi")
// `user`.`name` LIKE %modi%
name.Like("%modi%")
// `user`.`name` REGEXP .*
name.Regexp(".*")
// `user`.`name` FIND_IN_SET(`name`,"modi,jinzhu,zhangqiang")
name.FindInSet("modi,jinzhu,zhangqiang")
// `uesr`.`name` CONCAT("[",name,"]")
name.Concat("[", "]")
active := field.NewBool("user", "active")
// `user`.`active` = TRUE
active.Is(true)
// NOT `user`.`active`
active.Not()
// `user`.`active` AND TRUE
active.And(true)
birth := field.NewString("user", "birth")
// `user`.`birth` = ? (now)
birth.Eq(time.Now())
// DATE_ADD(`user`.`birth`, INTERVAL ? MICROSECOND)
birth.Add(time.Duration(time.Hour).Microseconds())
// DATE_FORMAT(`user`.`birth`, "%W %M %Y")
birth.DateFormat("%W %M %Y")
5.2 单值查询
GORM Gen 提供了三个查询单值的方法,First/Take/Last,默认都会加上limit 1的限制,同时在没有查到的时候会返回ErrRecordNotFound错误;其中First还会加上主键排序,Last会加上主键倒排。
如果就不希望返回这个找不到的错误,可以选择手动加Limit(1),然后用Find/Scan查询。
// SingleValueQuery 单值查询
func SingleValueQuery(ctx context.Context) {
u := query.User
// 查用户1的第一条
user, err := u.WithContext(ctx).Where(u.ID.Eq(1)).First()
// SELECT * FROM user WHERE `user`.`id` = 1 ORDER BY id LIMIT 1;
if err != nil {
logs.CtxError(ctx, "[SingleValueQuery] First err=%s", err.Error())
}
fmt.Println(user)
// 查一条,走数据库的默认排序
user, err = u.WithContext(ctx).Take()
// SELECT * FROM users LIMIT 1;
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
logs.CtxError(ctx, "[SingleValueQuery] Take err=%s", err.Error())
}
fmt.Println(user)
// 查用户1的最后一条
user, err = u.WithContext(ctx).Where(u.ID.Eq(1)).Last()
// SELECT * FROM user WHERE `user`.`id` = 1 ORDER BY id DESC LIMIT 1;
if err != nil {
logs.CtxError(ctx, "[SingleValueQuery] Last err=%s", err.Error())
}
fmt.Println(user)
// 走主库查询
user, err = u.WithContext(ctx).WriteDB().Last()
// check error ErrRecordNotFound
errors.Is(err, gorm.ErrRecordNotFound)
//如果不希望返回err,可以用limit+find/scan
users, err := u.WithContext(ctx).Where(u.ID.Eq(1)).Limit(1).Find()
if len(users) > 0 {
fmt.Println(users[0])
}
var User *model.User
err = u.WithContext(ctx).Where(u.ID.Eq(1)).Limit(1).Scan(&User)
fmt.Println(User)
}
5.3 简单查询
这部分主要是一些简单的查询使用,比如分页,统计、分组、JSON查询
// SimpleQuery 简单查询
func SimpleQuery(ctx context.Context) {
u := query.User
//单条件,用户1的数据
users, err := u.WithContext(ctx).Where(u.ID.Eq(1)).Limit(10).Find()
if err != nil {
logs.CtxError(ctx, "[SimpleQuery] Find err=%s", err.Error())
}
fmt.Println(users)
//统计
total, err := u.WithContext(ctx).Where(u.ID.Eq(1)).Count()
if err != nil {
logs.CtxError(ctx, "[SimpleQuery] Count err=%s", err.Error())
}
fmt.Println(total)
//分页查询,排序
users, total, err = u.WithContext(ctx).Where(u.ID.Eq(1)).Order(u.ID.Desc(), u.Phone).FindByPage(0, 10)
if err != nil {
logs.CtxError(ctx, "[SimpleQuery] Count err=%s", err.Error())
}
fmt.Println(total)
fmt.Println(users)
//多条件AND查询 用户1
users, total, err = u.WithContext(ctx).Where(u.Name.Eq("1")).FindByPage(0, 10)
//OR条件查询
// (`user`.`id` = 1 AND `user`.`name` = 1) OR (`user`.`id` = 2)
users, total, err = u.WithContext(ctx).Where(u.ID.Eq(1), u.Name.Eq("1")).Or(u.ID.Eq(2)).FindByPage(0, 10)
// 元组查询,多个字段的条件组合
//查询用户1类型 以及用户2类型2的
users, err = u.WithContext(ctx).Where(u.Columns(u.Name, u.Phone).In(field.Values([][]interface{}{{"1", "1"}, {"2", "2"}}))).Find()
// SELECT * FROM `user` WHERE (`user.name`, `user.phone`) IN ((1,1),(2,2);
//JSON查询
users, err = u.WithContext(ctx).Where(gen.Cond(datatypes.JSONQuery("extra").HasKey("xx_id"))...).Find()
// SELECT * FROM `user` WHERE JSON_EXTRACT(`attributes`,'$.user_id') IS NOT NULL;
//选择查询的字段
users, err = u.WithContext(ctx).Select(u.ID, u.Name).Where(u.ID.Eq(1)).Find()
//去重
users, err = u.WithContext(ctx).Distinct(u.ID, u.Phone).Order(u.ID, u.Name.Desc()).Find()
//分组
var Tuser []struct {
Name string
Total int
}
err = u.WithContext(ctx).Select(u.ID, u.ID.Count().As("total")).Group(u.ID).Scan(&Tuser)
//加上Having
err = u.WithContext(ctx).Select(u.ID, u.ID.Count().As("total")).Group(u.ID).Having(u.Name.Eq("10")).Scan(&Tuser)
}
5.4 复杂查询
复杂查询将会介绍,JOIN连表、子查询、查询创建等使用方式
// ComplexQuery 复杂查询
func ComplexQuery(ctx context.Context) {
u := query.User
c := query.Role
//连表查询(示例没有业务含义)
type Result struct {
RoleName string
Phone string
ID int64
}
var result Result
//和其他表连接
err := u.WithContext(ctx).Select(u.ID, u.Phone, c.Name.As("role_name")).LeftJoin(c, c.ID.EqCol(u.RoleID)).Scan(&result)
// SELECT `user`.`phone`,`role`.`name` as role_name FROM `user` left join `role` on `role`.`id` = `user`.`role_id`
if err != nil {
logs.CtxError(ctx, "[ComplexQuery] LeftJoin Scan err=%s", err.Error())
}
fmt.Println(result)
// 同一个表做连接
var result2 Result
u2 := u.As("u2")
err = u.WithContext(ctx).Select(u.ID, u2.Phone).LeftJoin(u2, u2.ID.EqCol(u.ID)).Scan(&result2)
// SELECT users.id, u2.phone FROM `user` left join `user` u2 on u2.id = user.id
//子查询的连接
var result3 Result
c2 := c.As("c2")
err = u.WithContext(ctx).Select(u.ID, c2.Name).LeftJoin(c.WithContext(ctx).Select(c.ID, c.Name).Where(c.ID.Gt(100)).As("c2"), c2.ID.EqCol(u.RoleID)).Scan(&result3)
// SELECT `user`.`id`,c2.`name` FROM `user` left join (select id,name from role where id > 100) as c2 on c2.id = `user`.`role_id`
}
//子查询
o := query.Order
u := query.User
orders, err := o.WithContext(ctx).Where(o.WithContext(ctx).Columns(o.Amount).Gt(o.WithContext(ctx).Select(o.Amount.Avg())).Find()
// SELECT * FROM "orders" WHERE amount > (SELECT AVG(amount) FROM "orders");
subQuery := u.WithContext(ctx).Select(u.Age.Avg()).Where(u.Name.Like("name%"))
users, err := u.WithContext(ctx).Select(u.Age.Avg().As("avgage")).Group(u.Name).Having(u.WithContext(ctx).Columns(u.Age.Avg()).Gt(subQuery).Find()
// SELECT AVG(age) as avgage FROM `users` GROUP BY `name` HAVING AVG(age) > (SELECT AVG(age) FROM `users` WHERE name LIKE "name%")
// Select users with orders between 100 and 200
subQuery1 := o.WithContext(ctx).Select(o.ID).Where(o.UserID.EqCol(u.ID), o.Amount.Gt(100))
subQuery2 := o.WithContext(ctx).Select(o.ID).Where(o.UserID.EqCol(u.ID), o.Amount.Gt(200))
u.WithContext(ctx).Exists(subQuery1).Not(u.WithContext(ctx).Exists(subQuery2)).Find()
// SELECT * FROM `users` WHERE EXISTS (SELECT `orders`.`id` FROM `orders` WHERE `orders`.`user_id`= `users`.`id` AND `orders`.`amount` > 100 AND `orders`.`deleted_at` IS NULL) AND NOT EXISTS (SELECT `orders`.`id` FROM `orders` WHERE `orders`.`user_id` = `users`.`id` AND `orders`.`amount` > 200 AND `orders`.`deleted_at` IS NULL) AND `users`.`deleted_at` IS NULL
//查询不存在赋值(不会创建)
// User not found, initialize it with given conditions and Attrs
u.WithContext(ctx).Attrs(field.Attrs(&model.User{Age: 20})).Where(u.Name.Eq("non_existing")).FirstOrInit()
// 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 given conditions and Attrs
u.WithContext(ctx).Attrs(u.Age.Value(20).Where(u.Name.Eq("non_existing")).FirstOrInit()
// SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1;
// user -> User{Name: "non_existing", Age: 20}
// Found user with `name` = `gen`, attributes will be ignored
u.WithContext(ctx).Attrs(field.Attrs(&model.User{Age: 20})).Where(u.Name.Eq("gen")).FirstOrInit()
// SELECT * FROM USERS WHERE name = 'gen' ORDER BY id LIMIT 1;
// user -> User{ID: 111, Name: "gen", Age: 18}
//查询不存在则创建
// User not found, initialize it with give conditions and Assign attributes
u.WithContext(ctx).Assign(field.Attrs(&model.User{Age: 20})).Where(u.Name.Eq("non_existing")).FirstOrCreate()
// 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` = `gen`, update it with Assign attributes
u.WithContext(ctx).Assign(field.Attrs(&model.User{Age: 20})).Where(u.Name.Eq("gen")).FirstOrCreate()
// SELECT * FROM users WHERE name = 'gen' ORDER BY id LIMIT 1;
// UPDATE users SET age=20 WHERE id = 111;
// user -> User{ID: 111, Name: "gen", Age: 20}
// Found user with `name` = `gen`, update it with Assign attributes
u.WithContext(ctx).Assign(u.Age.Value(20)).Where(u.Name.Eq("gen")).FirstOrCreate()
// SELECT * FROM users WHERE name = 'gen' ORDER BY id LIMIT 1;
// UPDATE users SET age=20 WHERE id = 111;
// user -> User{ID: 111, Name: "gen", Age: 20}
}
下一篇:GORM GEN的最佳实践