GoLong的学习之路(十七)基础工具之GORM(操作数据库)(更新)

news2024/11/24 11:49:46

书接上回,上回写道,GORM的查询和创建(插入数据),这回继续些增删改查的改和删的操作。

文章目录

  • 更新update
    • 修改单个列
    • 修改多个列
    • 修改选定字段
    • 批量更新新
      • 阻止全局更新
    • 使用 SQL 表达式更新
      • `注意`
    • 根据子查询进行更新
    • 不使用 Hook 和时间追踪
    • 返回修改行的数据
    • 检查字段是否有改变
    • 在 Update 时修改值
    • 其他手段更新(save)
  • 总结`注意`

更新update

修改单个列

当使用Update更新单个列时,它需要有任何条件,否则会引发错误ErrMissingWhereClause,详情查看Block Global Updates了解详细信息。当使用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;

当用户ID为111

// User's ID is `111`:
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;

更新条件和模型值

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]接口{}进行更新。

注意:
当使用struct进行更新时,默认情况下只更新非零字段

使用' 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来更新属性,或者使用Select来指定要更新的字段这种情况下

使用' 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;

修改选定字段

使用' map '更新字段 Select

db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;

使用' map '更新字段 Qmit

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;

使用Struct 更新所有字段包括非0字段

db.Model(&user).Select("*").Updates(User{Name: "李四", Role: "admin", Age: 0})
db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "李四", Role: "admin", Age: 0})

更新通知(钩子)
GORM允许钩子Beforeave, BeforeUpdate, AfterSave, AfterUpdate。这些方法将在更新记录时调用。

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

批量更新新

没有使用Model指定具有主键值的记录,GORM将执行批处理更新
使用struct进行更新

db.Model(User{}).Where("role = ?", "admin").Updates(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE role = 'admin';

使用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", "李四").Error // gorm.ErrMissingWhereClause

正确但是又不怎么正确的案例,比如:where 1=1

db.Model(&User{}).Where("1 = 1").Update("name", "李四")
// UPDATE users SET `name` = "李四" WHERE 1=1

正确方法

db.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "李四")
// UPDATE users SET `name` = "李四"
db.Exec("UPDATE users SET name = ?", "李四")
// UPDATE users SET name = "李四"

使用 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;

GORM还允许使用自定义数据类型更新SQL表达式/上下文值

注意

这种方式很特别

// Create from customized data type
type Location struct {
    X, Y int
}

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

db.Model(&User{ID: 1}).Updates(User{
  Name:  "jinzhu",
  Location: Location{X: 100, Y: 100},
})
// UPDATE `user_with_points` SET `name`="jinzhu",`location`=ST_PointFromText("POINT(100 100)") WHERE `id` = 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 = ?", "李四").Update("company_name", db.Table("companies as c").Select("name").Where("c.id = u.company_id"))

db.Table("users as u").Where("name = ?", "李四").Updates(map[string]interface{}{"company_name": db.Table("companies as c").Select("name").Where("c.id = u.company_id")})

不使用 Hook 和时间追踪

如果你想跳过Hooks方法并且在更新时不跟踪更新时间,你可以使用UpdateColumn UpdateColumns,它的工作原理就像update, Updates

更新单列

db.Model(&user).UpdateColumn("name", "hello")
// UPDATE users SET name='hello' WHERE id = 111;

更新多列

db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE id = 111;

更新选定的列

db.Model(&user).Select("name", "age").UpdateColumns(User{Name: "hello", Age: 0})
// UPDATE users SET name='hello', age=0 WHERE id = 111;

返回修改行的数据

返回所有行

var users []User
db.Model(&users).Clauses(clause.Returning{}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))
// UPDATE `users` SET `salary`=salary * 2,`updated_at`="2021-10-28 17:37:23.19" WHERE role = "admin" RETURNING *

返回指定列

db.Model(&users).Clauses(clause.Returning{Columns: []clause.Column{{Name: "name"}, {Name: "salary"}}}).Where("role = ?", "admin").Update("salary", gorm.Expr("salary * ?", 2))
// UPDATE `users` SET `salary`=salary * 2,`updated_at`="2021-10-28 17:37:23.19" WHERE role = "admin" RETURNING `name`, `salary`

检查字段是否有改变

GORM提供了Changed方法,可以在Before Update Hooks(通知)中使用,它将返回字段是否更改。

Changed方法只适用于Update、Updates方法,并且它只检查Update / Updates中的更新值是否等于模型值。如果它被改变并且没有被省略,它将返回true。

func (u *User) BeforeUpdate(tx *gorm.DB) (err error) {
  // if Role changed
    if tx.Statement.Changed("Role") {
    return errors.New("role not allowed to change")
    }

  if tx.Statement.Changed("Name", "Admin") { // if Name or Role changed
    tx.Statement.SetColumn("Age", 18)
  }

  // if any fields changed
    if tx.Statement.Changed() {
        tx.Statement.SetColumn("RefreshedAt", time.Now())
    }
    return nil
}

在 Update 时修改值

要更改Before Hooks中的更新值,你应该使用SetColumn,除非它是一个完整的保存更新和保存(save)

func (user *User) BeforeSave(tx *gorm.DB) (err error) {
  if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil {
    tx.Statement.SetColumn("EncryptedPassword", pw)
  }

  if tx.Statement.Changed("Code") {
    user.Age += 20
    tx.Statement.SetColumn("Age", user.Age)
  }
}

db.Model(&user).Update("Name", "李四")

其他手段更新(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;

Save是一个组合功能。如果save value不包含主键,它将执行Create,否则将执行Update(包含所有字段)。

db.Save(&User{Name: "jinzhu", Age: 100})
// INSERT INTO `users` (`name`,`age`,`birthday`,`update_at`) VALUES ("jinzhu",100,"0000-00-00 00:00:00","0000-00-00 00:00:00")

db.Save(&User{ID: 1, Name: "jinzhu", Age: 100})
// UPDATE `users` SET `name`="jinzhu",`age`=100,`birthday`="0000-00-00 00:00:00",`update_at`="0000-00-00 00:00:00" WHERE `id` = 1

注意
不要使用Save 关于 Model,这是一个错误用法

总结注意

在很多时候修改字段的默认值的时候会失败。其中我遇到过这样的一个情况。就是在使用更新操作的时候。
我们需要将一个非零的字段改成0,此时就出问题了。无法修改。

案例:

mysql.DB.Model(&entiy.Member{}).Where("id=", 6).Update("member_name", "0")
	mysql.DB.Model(&entiy.Member{}).Select("member_name").Where("id=?", 3).Updates(&entiy.Member{MemberName: "0"})
	mysql.DB.Model(&entiy.Member{}).Where("id = ? ", 4).Updates(map[string]interface{}{"member_name": "0"})
	mysql.DB.Save(&entiy.Member{
		MemberName: "0",
		ID:         5,
	})

运行前
在这里插入图片描述
运行后
在这里插入图片描述
可以发现6号是没有改变的。

推荐用前两种。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1163159.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Modbus转Profinet网关与流量变送器兼容转ModbusTCP协议博图配置案例

首先,我们需要明确电磁流量计的通信协议是Modbus,而西门子1200PLC的通信协议是Profinet。这两种协议在功能和特性上存在一定的差异,因此需要使用兴达易控Modbus转Profinet网关设备进行转换。兴达易控的XD-MDPN100是Profinet转ModbusTCP的网关…

功率放大器的种类和作用是什么

功率放大器是一种电子设备,用于将输入信号的功率增加到更高的水平,以驱动负载或输出设备。功率放大器广泛应用于各种领域,包括通信、音频、无线电频谱分析、激光器和雷达等。 根据应用需求和工作原理不同,功率放大器可分为几种不同…

笔记:IDEA如何修改代码后,不重启服务器局部更新资源

前言 平常用IDEA开发网页写调样式和测功能最讨厌改一丁点东西就要重启整个服务器,所以本文主要就是解决此问题从而提高开发效率,避免浪费过多时间。 具体步骤 1、打开设置框 2、先新增exploded结尾的,并apply应用,把没有结尾的…

【Kubernetes部署】二进制部署单Master Kurbernetes集群 超详细

二进制部署K8s 一、基本架构和系统初始化操作1.1 基本架构1.2 系统初始化操作 二、部署etcd集群2.1 证书签发Step1 下载证书制作工具Step2 创建k8s工作目录Step3 编写脚本并添加执行权限Step4 生成CA证书、etcd 服务器证书以及私钥 2.2 启动etcd服务Step1 上传并解压代码包Step…

云尘-Node1 js代码

继续做题 拿到就是基本扫一下 nmap -sP 172.25.0.0/24 nmap -sV -sS -p- -v 172.25.0.13 然后顺便fscan扫一下咯 nmap: fscan: 还以为直接getshell了 老演员了 其实只是302跳转 所以我们无视 只有一个站 直接看就行了 扫出来了两个目录 但是没办法 都是要跳转 说明还是需要…

轻松搭建Nextcloud私有云盘并实现远程访问【内网穿透】

文章目录 摘要1. 环境搭建2. 测试局域网访问3. 内网穿透3.1 ubuntu本地安装cpolar3.2 创建隧道3.3 测试公网访问 4 配置固定http公网地址4.1 保留一个二级子域名4.1 配置固定二级子域名4.3 测试访问公网固定二级子域名 摘要 Nextcloud,它是ownCloud的一个分支,是一个文件共享服…

opencv第一个例子

目的 这是用用QTopencv实现的一个完整的展示图片的例子,包括了项目的配置文件,完整的代码,以用做初次学习opencv用。 代码 工程文件: QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET openCv1 TEMPL…

双路比例阀放大器

双路比例阀放大器是一种常见的电子设备,它能够将输入信号放大到所需的水平,并输出两个相等或不同的放大信号。这种放大器通常由一个放大器和一个驱动电路组成,可以用于各种应用中,如液压控制、气动控制等。 在液压控制方面&#…

物联网系统的基本构件

1.基本组件 云服务器 数据库消息服务器应用服务器管理平台 云APP 云服务器的维护终端微信客户端网页管理平台 页面式的更全面的管理。组态软件和PLC软件 编程软件终端设备 PLC 主要指标,模拟数字接口数量 DO有 继电器和1.5,2.5.5V数字输出一般支持扩展IO模块模拟量…

利用win32的GetLastInputInfo函数实现锁屏(C#)

前两天看到群里面讨论这个问题,刚好我们上一家公司的系统也有这个功能,就研究了一下,我们这边实现这个功能的目的如下:当用户长时间不操作系统时,自动退出系统并退回到登录界面,想要使用系统,就…

软文投放、发稿:如何写一篇优质的软文

在当今的营销世界中,软文是一种强大的工具,可以用来宣传产品、建立品牌形象,以及与受众建立更深层次的联系。然而,要写一篇优质的软文并不容易。本文将介绍如何撰写一篇引人入胜的软文,以吸引读者的兴趣和赢得他们的信…

用 Java 实现 Syslog 功能

1、业务场景 用一个 Spring Boot 的项目去实现对管控设备的监控、日志收集等。同时需要将接收到的日志进行入库,每天存一张表,如device_log_20231026… 2、Syslog客户端(接收日志的服务器,即运行Java程序的服务器) 2…

JavaScript 基础 - 第4天

理解封装的意义,能够通过函数的声明实现逻辑的封装,知道对象数据类型的特征,结合数学对象实现简单计算功能。 理解函数的封装的特征掌握函数声明的语法理解什么是函数的返回值知道并能使用常见的内置函数 函数 理解函数的封装特性&#xff0c…

软件测试/测试开发丨UbuntuServer环境准备

点此获取更多相关资料 前提 现有设备是一套 i54090 的组合,安装了 Ubuntu 22.04.3 LTS Server 版本,后文的安装步骤都是基于这套系统和配置进行操作。 系统准备 查看是否安装了 gcc 命令行中执行 gcc -v 正常输入如图效果的,说明已经成功…

kubeadm部署kubernetes1.28

k8s在1.24版本以后删除了内置dockershim插件,原生不再支持docker运行时,需要使用第三方cri接口cri-docker https://github.com/Mirantis/cri-dockerd.git 安装前,需要先升级systemd和主机内核,本操作文档安装的是最新的版本kube…

微信小程序渲染的富文本里面除了img标签外什么都没有,该如何设置img的大小

微信小程序富文本渲染&#xff1a; <rich-text nodes"{{content}}"style"{{style}}" ></rich-text> content是接口得到的值 let cont object.contentlet a cont.replace(/<img/gi,<img style"max-width:94%;height:auto;margi…

大厂面试题-什么是IO的多路复用机制?

IO多路复用机制&#xff0c;核心思想是让单个线程去监视多个连接&#xff0c;一旦某个连接就绪&#xff0c;也就是触发了读/写事件。 就通知应用程序&#xff0c;去获取这个就绪的连接进行读写操作。 也就是在应用程序里面可以使用单个线程同时处理多个客户端连接&#xff0c…

四川竹哲电子商务有限公司服务怎么样?

随着抖音电商的日益崛起&#xff0c;越来越多的商家开始关注这个充满无限商机的平台。四川竹哲电子商务有限公司作为一家专业的抖音电商服务公司&#xff0c;凭借其丰富的经验和优秀的服务&#xff0c;成为了众多商家在抖音电商领域中的重要合作伙伴。 一、专业实力 作为一家专…

同为科技(TOWE)国标10A电瓶车专用智能定时桌面PDU插线板

电动车作为现在国内保有量较高的一种交通工具&#xff0c;因其价格亲民、环保便捷、使用成本低等原因受到广大人民群众的欢迎。然而&#xff0c;电瓶车充电问题长期以来是广受关注的社会性话题&#xff0c;过充短路爆充引起火灾事故的新闻时有发生&#xff0c;80%的电动车火灾都…

将流程作为战略丨看看流程智能在食品巨头“百事”的应用场景

“更快、更好、更强”是百事可乐的使命。百事公司GPEX&#xff08;全球流程卓越组织&#xff09;的使命则是&#xff1a;改造、数字化和加强百事公司的端到端流程&#xff0c;使每个流程都具备相关的可衡量价值。 目前&#xff0c;百事公司正在使用预测分析、人工智能、机器人…