为安全性保证、防止误操作,UpdateDelete方法必须带有Where条件才能提交执行,否则将会错误返回,错误信息如:there should be WHERE condition statement for XXX operationgoframe是一款用于企业生产级别的框架,各个模块设计严谨,工程实践的细节处理得比较好。

Update更新方法

Update用于数据的更新,往往需要结合DataWhere方法共同使用。Data方法用于指定需要更新的数据,Where方法用于指定更新的条件范围。同时,Update方法也支持直接给定数据和条件参数。

使用示例:

// UPDATE `user` SET `name`='john guo' WHERE name='john'
g.Model("user").Data(g.Map{"name" : "john guo"}).Where("name", "john").Update()
g.Model("user").Data("name='john guo'").Where("name", "john").Update()

// UPDATE `user` SET `status`=1 WHERE `status`=0 ORDER BY `login_time` asc LIMIT 10
g.Model("user").Data("status", 1).Order("login_time asc").Where("status", 0).Limit(10).Update()

// UPDATE `user` SET `status`=1 WHERE 1
g.Model("user").Data("status=1").Where(1).Update()
g.Model("user").Data("status", 1).Where(1).Update()
g.Model("user").Data(g.Map{"status" : 1}).Where(1).Update()

也可以直接给Update方法传递datawhere参数:

// UPDATE `user` SET `name`='john guo' WHERE name='john'
g.Model("user").Update(g.Map{"name" : "john guo"}, "name", "john")
g.Model("user").Update("name='john guo'", "name", "john")

// UPDATE `user` SET `status`=1 WHERE 1
g.Model("user").Update("status=1", 1)
g.Model("user").Update(g.Map{"status" : 1}, 1)

Counter更新特性

可以使用Counter类型参数对特定的字段进行数值操作,例如:增加、减少操作。

Counter数据结构定义:

// Counter  is the type for update count.
type Counter struct {
	Field string
	Value float64
}

Counter使用示例,字段自增:

updateData := g.Map{
	"views": &gdb.Counter{ 
        Field: "views", 
        Value: 1,
    },
}
// UPDATE `article` SET `views`=`views`+1 WHERE `id`=1
result, err := db.Update("article", updateData, "id", 1)

Counter也可以实现非自身字段的自增,例如:

updateData := g.Map{
	"views": &gdb.Counter{ 
        Field: "clicks", 
        Value: 1,
    },
}
// UPDATE `article` SET `views`=`clicks`+1 WHERE `id`=1
result, err := db.Update("article", updateData, "id", 1)

Increment/Decrement自增/减

我们可以通过IncrementDecrement方法实现对指定字段的自增/自减常用操作。两个方法的定义如下:

// Increment increments a column's value by a given amount.
func (m *Model) Increment(column string, amount float64) (sql.Result, error)

// Decrement decrements a column's value by a given amount.
func (m *Model) Decrement(column string, amount float64) (sql.Result, error)

使用示例:

// UPDATE `article` SET `views`=`views`+10000 WHERE `id`=1
g.Model("article").Where("id", 1).Increment("views", 10000)
// UPDATE `article` SET `views`=`views`-10000 WHERE `id`=1
g.Model("article").Where("id", 1).Decrement("views", 10000)

RawSQL语句嵌入

gdb.Raw是字符串类型,该类型的参数将会直接作为SQL片段嵌入到提交到底层的SQL语句中,不会被自动转换为字符串参数类型、也不会被当做预处理参数。更详细的介绍请参考章节:ORM高级特性-RawSQL。例如:

// UPDATE `user` SET login_count='login_count+1',update_time='now()' WHERE id=1
g.Model("user").Data(g.Map{
    "login_count": "login_count+1",
    "update_time": "now()",
}).Where("id", 1).Update()
// 执行报错:Error Code: 1136. Column count doesn't match value count at row 1

使用gdb.Raw改造后:

// UPDATE `user` SET login_count=login_count+1,update_time=now() WHERE id=1
g.Model("user").Data(g.Map{
    "login_count": gdb.Raw("login_count+1"),
    "update_time": gdb.Raw("now()"),
}).Where("id", 1).Update()

Delete删除方法

Delete方法用于数据的删除。

使用示例:

// DELETE FROM `user` WHERE uid=10
g.Model("user").Where("uid", 10).Delete()
// DELETE FROM `user` ORDER BY `login_time` asc LIMIT 10
g.Model("user").Order("login_time asc").Limit(10).Delete()

也可以直接给Delete方法传递where参数:

// DELETE FROM `user` WHERE `uid`=10
g.Model("user").Delete("uid", 10)
// DELETE FROM `user` WHERE `score`<60
g.Model("user").Delete("score < ", 60)

软删除特性

软删除特性请查看章节:ORM链式操作-时间维护








Content Menu

  • No labels

34 Comments

  1. gdbCounter := &gdb.Counter{
    	Field: "views",
    	Value: 1,
    }

    是不可以理解为 views 字段加 1, 如果想要 -1 则写成:

    gdbCounter := &gdb.Counter{ Field: "views", Value: -1 }
    1. 个人感觉挺麻烦的..做自减都要做一次负数转化.还是常规链式的Inc/Dec机制比较区分.

      1. 负数转化?

        1. 例如会员消费余额,前端传回来是个正数(消费多少),扣除余额的时候在Value之前就要对这个金额做一次转换使之变为负数(在前面加个减号).

  2. Counter使用示例

    能否加个 sql语句 这样容易理解

    1. 感谢建议,已更新。

  3. 能否实现类似LaravelUpdateOrCreate

    https://laravel.com/docs/8.x/eloquent#upserts

    如果存在就更新,否则插入数据

    每次都要重复写这些逻辑,或者是要在每个dao里面去拷贝代码,感觉不太优雅

    1. 哥,就是Save方法。😓

      1. 感觉不对,这个差异很大,比如表结构

        id, name, date, score, created_at, updated_at
        namedate是唯一索引

        dao.Score.Data(g.Map{
            dao.Score.Columns.Date:  "2021-01-18",
            dao.Score.Columns.Name:  "test",
            dao.Score.Columns.Score: 80,
        }).Save()

        此时会Insert一条新记录

        但是实际意图是:

        如果存在 name="test" and date="2021-01-18" 的记录,则更新其score80

        否则插入 name="test", date="2021-01-18", score=80

        func (r *ScoreDao) UpdateOrCreate(whereAndData ...g.Map) {}
        
        dao.Score.UpdateOrCreate(g.Map{
           dao.Score.Columns.Date:   "2021-01-18",
           dao.Score.Columns.Name: "test",
        }, g.Map{
           dao.Score.Columns.Score: 80,
        })




        1. 现在Save的逻辑就是这样啊,你把debug打开看看sql对不对:ORM链式操作-写入保存

          1. 谢谢💕,由于debug显示的SQL较长,没注意看后面的 ON DUPLICATE KEY UPDATE,只看到前面的 INSERT INTO

  4. xx

    有没有mysql更新json类型字段的例子?

  5. 请问一下,删除了以后怎样检查删除操作是否执行成功呢?谢谢了!

    1. 判断返回的Result结果,里面有RowsAffected方法。

      1. 明白了,感谢!

  6. db.Table("user").Data("status", 1).Order("login_time asc").Limit(10).Update()

    我在测试这条语句时,没有生效,请问是我的数据表有问题吗?

    我的数据表如下

    idnicknamepasswordpassport
    1test0001123456test0001
    2test0002123456test0002
    g.DB().Table("user").Data("password", "llllllll").Order("id asc").Limit(10).Update()
    我在执行这条语句后,发现库里的数据没有变化。
    go 版本 1.16
    goframe版本 1.16.2
    1. 有db.Table方法吗?不是g.DB().Model("user")吗?
  7. 我在测试以下示例时,用debug看到的sql对不上

    updateData := g.Map{
    	"views": &gdb.Counter{ 
            Field: "clicks", 
            Value: 1,
        },
    }
    // UPDATE `article` SET `views`=`clicks`+1 WHERE `id`=1
    result, err := db.Update("article", updateData, "id", 1)


    我的例子如下

    updateData := g.Map{
    "views": &gdb.Counter{
    Field: "clicks",
    Value: 1,
    },
    }
    g.DB().Update("user", updateData, "id", 3)
    得到的debug信息为:
    UPDATE `user` SET `clicks`=`clicks`+1,`update_at`='2021-07-13 15:02:58' WHERE `id`=3 
    更新的字段不是views,而是clicks。

    1. 嗯,试试最新的master分支。

  8. 好像Delete没有对Batch做支持.mysql下占位符只能到65535个,假设批量删除超过这个数直接就报错了,这里应该让Batch方法生效.

    1. 可以把代码提个issue我看看呢。

      1.  all,_:=g.Model("table").Array("filed")或者手动生成一个长度大于65535的数组做为删除的where参数.

        只要len(all)大于65535时直接执行g.Model("table").Where("filed", all).Delete()则返回mysql的错误信息Prepared statement contains too many placeholders.需要将all手动切割成两部分在执行delete.

        假设删除10240条记录执行g.Model("table").Where("filed", all).Batch(1024).Delete() 每次批量删除1024条记录,执行10次.(当前版本对delete执行Batch链式方法也不会再执行删除时做数组切割,即不论是否显式调用Batch都对delete无影响)


      2. 感觉在ORM执行前应该对参数(占位符)长度做下与判断,如果超长了可以转为默认批处理模式(触发场景不多,基本都发生在batch insert与where in delete)以提升ORM的健壮性.实际用户逻辑并没有错,但会直接返回占位符过长错误.

  9. 我现在有一个需求,就是要update完之后返回此次update的ids 怎么搞没看见累死的returning函数

  10. 请问一下,为什么目前不支持如下的语法,新人用起来很反直觉: 
    `g
    .Model("it_lend_bonus_daily").Where("day_time = ?", day).Update("total_amount = total_amount + ?, updated_at = ?", amount, gtime.Now().TimestampMilli())`
    报错如下:
    `UPDATE `it_lend_bonus_daily` SET total_amount = total_amount + 19422, updated_at = 1678088562436 WHERE (`day_time`=?) AND (`5000000`=?)
    Error: Error 1054: Unknown column '5000000' in 'where clause'`
  11. 表结构带着update_at结果:answers update failed: UPDATE `answers` SET content='2023-04-28 14:50:20', user_id='<p><span style="color: rgb(34, 34, 34); background-color: rgb(255, 255, 255); font-size: 16px;">当父子元素中都有点击事</p><p><br></p>',update_at='1318027088967503872' WHERE id=9: Error 1406: Data too long for column 'user_id' at row 1,update_at不是自动更新的吗,结果现在更新字段怎么错乱了,昨天还好好的

  12. JB

    g.Model("user").Data(map[string]interface{}{"table.field": "aaaa"})
    联表更新时,字段会被解析成`table.field`,正确的应该是`table`.`field`

  13. 这里写错了吧

    g.Model("user").Data("status", 1).Order("login_time asc").Limit(10).Update() 错误
    g.Model("user").Data("status", 1).Order("login_time asc").Limit(10).Where(1).Update() 正确
    1. 感谢反馈,文档已修正。

  14. 大佬, 外连接删除没有对应封装吗, 

    如:  delete u from app_user u left join app_goods g on g.id=u.goods_id where g.type="mine",

    使用Model.Delete实际sql是delete from app_user u left join app_goods g on g.id=u.goods_id where g.type="mine", 对mysql会报错, 缺了类似fields那里

  15. 在mssql数据库场景下,有报错如下
    2024-05-31 16:20:13.350 [ERRO] {4c8e00b40485d417f8b34d58e6bfcb91} [ 56 ms] [default] [OGSCMDB] [rows:0  ] [txid:3] UPDATE tools_gen_table SET table_id=134,table_name='demo_gen_class',table_comment='班级',class_name='DemoGenClass',tpl_category='crud',package_name='internal/app/demo',module_name='demo',business_name='demo_gen_class',function_name='demo班级',function_author='gfast',options='',create_time='2024-05-31 16:15:03',update_time='2024-05-31 16:20:13',remark='',overwrite=true,sort_column='id',sort_type='asc',show_detail=false,excel_port=false,use_snow_id=false,use_virtual=false,excel_imp=false,overwrite_info='[{"key":"api","value":true},{"key":"controller","value":true},{"key":"dao","value":true},{"key":"dao_internal","value":true},{"key":"logic","value":true},{"key":"model","value":true},{"key":"model_do","value":true},{"key":"model_entity","value":true},{"key":"router","value":true},{"key":"router_func","value":true},{"key":"service","value":true},{"key":"sql","value":true},{"key":"tsApi","value":true},{"key":"tsModel","value":true},{"key":"vue","value":true},{"key":"vueDetail","value":true},{"key":"vueEdit","value":true}]',son_table_ids='[]' WHERE table_id=134
    Error: mssql: 无法更新标识列 'table_id'。 
    源码大概如下
    err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
    err = g.Try(ctx, func(ctx context.Context) {
    _, err = tx.Model(dao.ToolsGenTable.Table()).
    WherePri(table.TableId).
    Update(table)
    liberr.ErrIsNil(ctx, err, "保存表数据失败")
    }
    }

    将打印的sql语句

    UPDATE tools_gen_table SET table_id=134,table_name='demo_gen_class',table_comment='班级',class_name='DemoGenClass',tpl_category='crud',package_name='internal/app/demo',module_name='demo',business_name='demo_gen_class',function_name='demo班级',function_author='gfast',options='',create_time='2024-05-31 16:15:03',update_time='2024-05-31 16:20:13',remark='',overwrite=true,sort_column='id',sort_type='asc',show_detail=false,excel_port=false,use_snow_id=false,use_virtual=false,excel_imp=false,overwrite_info='[{"key":"api","value":true},{"key":"controller","value":true},{"key":"dao","value":true},{"key":"dao_internal","value":true},{"key":"logic","value":true},{"key":"model","value":true},{"key":"model_do","value":true},{"key":"model_entity","value":true},{"key":"router","value":true},{"key":"router_func","value":true},{"key":"service","value":true},{"key":"sql","value":true},{"key":"tsApi","value":true},{"key":"tsModel","value":true},{"key":"vue","value":true},{"key":"vueDetail","value":true},{"key":"vueEdit","value":true}]',son_table_ids='[]' WHERE table_id=134
    放入mssql的客户端调试主要问题为bool型的不能填true false,必须是1,0,因为数据库中无bool类型设置的是bit类型,还有主键table_id不能修改等两个问题,请问是我的使用问题还是框架不支持呢?
  16. 2024-06-14 01:38:26.126 [ERRO] [  0 ms] [default] UPDATE `trace_enterprise_info` SET `production_license`='1',`create_by`='admin',`update_by`='admin',`description`='1',`contact_mobile`='1',`material_id`='548c7972adeb4756add92dbefe40e3a3',`name`='test1',`relation_type`='single',`update_time`='2024-06-14 01:38:22',`id`='4491702624024d6481e7179258cab9be',`service_mobile`='1',`create_time`='2024-06-08 21:24:22',`license`='1',`business_license`='1' WHERE `id` IN ('4491702624024d6481e7179258cab9be')
    Error: sql: expected 15 arguments, got 16 
    
    

    这样更新为啥更新不了啊?

    // Update does "UPDATE...WHERE..." statement for updating current object from table.
    // It updates the record if there's already another same record in the table
    // (it checks using primary key or unique index).
    func (r *Entity) Update() (result sql.Result, err error) {
    	return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update()
    }

    我通过sql 又可以重新更新

  17. 数据操作框架感觉是有问题啊:

    怎么会有一个空素组啊?